--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Commit 17c4febc96c145692d730ea3728e7a6d6c363e4e
Parents : dcf722d
Author : Mark Qvist <mark@unsigned.io>
Date : 2024-06-03T01:54:58+02:00
Added pyogg
Changes
19 files changed, 7500 insertions(+), 0 deletions(-)
Diff
diff --git a/sbapp/pyogg/__init__.py b/sbapp/pyogg/__init__.py
new file mode 100644
index 00000000..a97b0d21
--- /dev/null
+++ b/sbapp/pyogg/__init__.py
@@ -0,0 +1,108 @@
+import ctypes
+
+from .pyogg_error import PyOggError
+from .ogg import PYOGG_OGG_AVAIL
+from .vorbis import PYOGG_VORBIS_AVAIL, PYOGG_VORBIS_FILE_AVAIL, PYOGG_VORBIS_ENC_AVAIL
+from .opus import PYOGG_OPUS_AVAIL, PYOGG_OPUS_FILE_AVAIL, PYOGG_OPUS_ENC_AVAIL
+from .flac import PYOGG_FLAC_AVAIL
+
+
+#: PyOgg version number. Versions should comply with PEP440.
+__version__ = '0.7'
+
+
+if (PYOGG_OGG_AVAIL and PYOGG_VORBIS_AVAIL and PYOGG_VORBIS_FILE_AVAIL):
+ # VorbisFile
+ from .vorbis_file import VorbisFile
+ # VorbisFileStream
+ from .vorbis_file_stream import VorbisFileStream
+
+else:
+ class VorbisFile: # type: ignore
+ def __init__(*args, **kw):
+ if not PYOGG_OGG_AVAIL:
+ raise PyOggError("The Ogg library wasn't found or couldn't be loaded (maybe you're trying to use 64bit libraries with 32bit Python?)")
+ raise PyOggError("The Vorbis libraries weren't found or couldn't be loaded (maybe you're trying to use 64bit libraries with 32bit Python?)")
+
+ class VorbisFileStream: # type: ignore
+ def __init__(*args, **kw):
+ if not PYOGG_OGG_AVAIL:
+ raise PyOggError("The Ogg library wasn't found or couldn't be loaded (maybe you're trying to use 64bit libraries with 32bit Python?)")
+ raise PyOggError("The Vorbis libraries weren't found or couldn't be loaded (maybe you're trying to use 64bit libraries with 32bit Python?)")
+
+
+
+if (PYOGG_OGG_AVAIL and PYOGG_OPUS_AVAIL and PYOGG_OPUS_FILE_AVAIL):
+ # OpusFile
+ from .opus_file import OpusFile
+ # OpusFileStream
+ from .opus_file_stream import OpusFileStream
+
+else:
+ class OpusFile: # type: ignore
+ def __init__(*args, **kw):
+ if not PYOGG_OGG_AVAIL:
+ raise PyOggError("The Ogg library wasn't found or couldn't be loaded (maybe you're trying to use 64bit libraries with 32bit Python?)")
+ if not PYOGG_OPUS_AVAIL:
+ raise PyOggError("The Opus library wasn't found or couldn't be loaded (maybe you're trying to use 64bit libraries with 32bit Python?)")
+ if not PYOGG_OPUS_FILE_AVAIL:
+ raise PyOggError("The OpusFile library wasn't found or couldn't be loaded (maybe you're trying to use 64bit libraries with 32bit Python?)")
+ raise PyOggError("Unknown initialisation error")
+
+ class OpusFileStream: # type: ignore
+ def __init__(*args, **kw):
+ if not PYOGG_OGG_AVAIL:
+ raise PyOggError("The Ogg library wasn't found or couldn't be loaded (maybe you're trying to use 64bit libraries with 32bit Python?)")
+ if not PYOGG_OPUS_AVAIL:
+ raise PyOggError("The Opus library wasn't found or couldn't be loaded (maybe you're trying to use 64bit libraries with 32bit Python?)")
+ if not PYOGG_OPUS_FILE_AVAIL:
+ raise PyOggError("The OpusFile library wasn't found or couldn't be loaded (maybe you're trying to use 64bit libraries with 32bit Python?)")
+ raise PyOggError("Unknown initialisation error")
+
+
+if PYOGG_OPUS_AVAIL:
+ # OpusEncoder
+ from .opus_encoder import OpusEncoder
+ # OpusBufferedEncoder
+ from .opus_buffered_encoder import OpusBufferedEncoder
+ # OpusDecoder
+ from .opus_decoder import OpusDecoder
+
+else:
+ class OpusEncoder: # type: ignore
+ def __init__(*args, **kw):
+ raise PyOggError("The Opus library wasn't found or couldn't be loaded (maybe you're trying to use 64bit libraries with 32bit Python?)")
+
+ class OpusBufferedEncoder: # type: ignore
+ def __init__(*args, **kw):
+ raise PyOggError("The Opus library wasn't found or couldn't be loaded (maybe you're trying to use 64bit libraries with 32bit Python?)")
+
+ class OpusDecoder: # type: ignore
+ def __init__(*args, **kw):
+ raise PyOggError("The Opus library wasn't found or couldn't be loaded (maybe you're trying to use 64bit libraries with 32bit Python?)")
+
+if (PYOGG_OGG_AVAIL and PYOGG_OPUS_AVAIL):
+ # OggOpusWriter
+ from .ogg_opus_writer import OggOpusWriter
+
+else:
+ class OggOpusWriter: # type: ignore
+ def __init__(*args, **kw):
+ if not PYOGG_OGG_AVAIL:
+ raise PyOggError("The Ogg library wasn't found or couldn't be loaded (maybe you're trying to use 64bit libraries with 32bit Python?)")
+ raise PyOggError("The Opus library was't found or couldn't be loaded (maybe you're trying to use 64bit libraries with 32bit Python?)")
+
+
+if PYOGG_FLAC_AVAIL:
+ # FlacFile
+ from .flac_file import FlacFile
+ # FlacFileStream
+ from .flac_file_stream import FlacFileStream
+else:
+ class FlacFile: # type: ignore
+ def __init__(*args, **kw):
+ raise PyOggError("The FLAC libraries weren't found or couldn't be loaded (maybe you're trying to use 64bit libraries with 32bit Python?)")
+
+ class FlacFileStream: # type: ignore
+ def __init__(*args, **kw):
+ raise PyOggError("The FLAC libraries weren't found or couldn't be loaded (maybe you're trying to use 64bit libraries with 32bit Python?)")
diff --git a/sbapp/pyogg/audio_file.py b/sbapp/pyogg/audio_file.py
new file mode 100644
index 00000000..4fb77a2e
--- /dev/null
+++ b/sbapp/pyogg/audio_file.py
@@ -0,0 +1,59 @@
+from .pyogg_error import PyOggError
+
+class AudioFile:
+ """Abstract base class for audio files.
+
+ This class is a base class for audio files (such as Vorbis, Opus,
+ and FLAC). It should not be instatiated directly.
+ """
+
+ def __init__(self):
+ raise PyOggError("AudioFile is an Abstract Base Class "+
+ "and should not be instantiated")
+
+ def as_array(self):
+ """Returns the buffer as a NumPy array.
+
+ The shape of the returned array is in units of (number of
+ samples per channel, number of channels).
+
+ The data type is either 8-bit or 16-bit signed integers,
+ depending on bytes_per_sample.
+
+ The buffer is not copied, but rather the NumPy array
+ shares the memory with the buffer.
+
+ """
+ # Assumes that self.buffer is a one-dimensional array of
+ # bytes and that channels are interleaved.
+
+ import numpy # type: ignore
+
+ assert self.buffer is not None
+ assert self.channels is not None
+
+ # The following code assumes that the bytes in the buffer
+ # represent 8-bit or 16-bit signed ints. Ensure the number of
+ # bytes per sample matches that assumption.
+ assert self.bytes_per_sample == 1 or self.bytes_per_sample == 2
+
+ # Create a dictionary mapping bytes per sample to numpy data
+ # types
+ dtype = {
+ 1: numpy.int8,
+ 2: numpy.int16
+ }
+
+ # Convert the ctypes buffer to a NumPy array
+ array = numpy.frombuffer(
+ self.buffer,
+ dtype=dtype[self.bytes_per_sample]
+ )
+
+ # Reshape the array
+ return array.reshape(
+ (len(self.buffer)
+ // self.bytes_per_sample
+ // self.channels,
+ self.channels)
+ )
diff --git a/sbapp/pyogg/flac.py b/sbapp/pyogg/flac.py
new file mode 100644
index 00000000..d44509ea
--- /dev/null
+++ b/sbapp/pyogg/flac.py
@@ -0,0 +1,2061 @@
+############################################################
+# Flac license: #
+############################################################
+"""
+Copyright (C) 2000-2009 Josh Coalson
+Copyright (C) 2011-2016 Xiph.Org Foundation
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+- Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+- Neither the name of the Xiph.org Foundation nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""
+
+import ctypes
+from ctypes import c_int, c_int8, c_int16, c_int32, c_int64, c_uint, c_uint8, c_uint16, c_uint32, c_uint64, c_float, c_long, c_ulong, c_char, c_bool, c_char_p, c_ubyte, c_longlong, c_ulonglong, c_size_t, c_void_p, c_double, POINTER, pointer, cast, CFUNCTYPE, Structure, Union
+import ctypes.util
+import sys
+from traceback import print_exc as _print_exc
+import os
+
+from .ogg import *
+
+from .library_loader import ExternalLibrary, ExternalLibraryError
+
+__here = os.getcwd()
+
+libflac = None
+
+try:
+ names = {
+ "Windows": "libFLAC.dll",
+ "Darwin": "libFLAC.8.dylib",
+ "external": "FLAC"
+ }
+ libflac = Library.load(names, tests = [lambda lib: hasattr(lib, "FLAC__EntropyCodingMethodTypeString")])
+except ExternalLibraryError:
+ pass
+except:
+ _print_exc()
+
+if libflac:
+ PYOGG_FLAC_AVAIL = True
+else:
+ PYOGG_FLAC_AVAIL = False
+
+# ctypes
+c_ubyte_p = POINTER(c_ubyte)
+c_uchar_p = c_ubyte_p
+c_uint_p = POINTER(c_uint)
+c_size_t_p = POINTER(c_size_t)
+c_off_t = c_int32
+# /ctypes
+
+if PYOGG_FLAC_AVAIL:
+ # Sanity check also satisfies mypy type checking
+ assert libflac is not None
+
+ # ordinals
+
+ FLAC__int8 = c_int8
+ FLAC__uint8 = c_uint8
+
+ FLAC__int16 = c_int16
+
+ FLAC__int32 = c_int32
+ FLAC__int32_p = POINTER(FLAC__int32)
+
+ FLAC__int64 = c_int64
+ FLAC__uint16 = c_uint16
+ FLAC__uint32 = c_uint32
+ FLAC__uint64 = c_uint64
+
+ FLAC__uint64_p = POINTER(FLAC__uint64)
+
+ FLAC__bool = c_bool
+
+ FLAC__byte = c_uint8
+
+ FLAC__byte_p = POINTER(FLAC__byte)
+
+ c_char_p_p = POINTER(c_char_p)
+
+ # /ordinals
+
+ # callback
+
+ FLAC__IOHandle = CFUNCTYPE(c_void_p)
+
+ FLAC__IOCallback_Read = CFUNCTYPE(c_size_t,
+ c_void_p,
+ c_size_t,
+ c_size_t,
+ FLAC__IOHandle)
+
+ FLAC__IOCallback_Write = CFUNCTYPE(c_size_t, c_void_p, c_size_t, c_size_t, FLAC__IOHandle)
+
+ FLAC__IOCallback_Seek = CFUNCTYPE(c_int, FLAC__IOHandle, FLAC__int64, c_int)
+
+ FLAC__IOCallback_Tell = CFUNCTYPE(FLAC__int64, FLAC__IOHandle)
+
+ FLAC__IOCallback_Eof = CFUNCTYPE(c_int, FLAC__IOHandle)
+
+ FLAC__IOCallback_Close = CFUNCTYPE(c_int, FLAC__IOHandle)
+
+ class FLAC__IOCallbacks(Structure):
+ _fields_ = [("read", FLAC__IOCallback_Read),
+ ("write", FLAC__IOCallback_Write),
+ ("seek", FLAC__IOCallback_Seek),
+ ("tell", FLAC__IOCallback_Tell),
+ ("eof", FLAC__IOCallback_Eof),
+ ("close", FLAC__IOCallback_Close)]
+
+ # /callback
+
+ # format
+
+ FLAC__MAX_METADATA_TYPE_CODE =(126)
+ FLAC__MIN_BLOCK_SIZE =(16)
+ FLAC__MAX_BLOCK_SIZE =(65535)
+ FLAC__SUBSET_MAX_BLOCK_SIZE_48000HZ =(4608)
+ FLAC__MAX_CHANNELS =(8)
+ FLAC__MIN_BITS_PER_SAMPLE =(4)
+ FLAC__MAX_BITS_PER_SAMPLE =(32)
+ FLAC__REFERENCE_CODEC_MAX_BITS_PER_SAMPLE =(24)
+ FLAC__MAX_SAMPLE_RATE =(655350)
+ FLAC__MAX_LPC_ORDER =(32)
+ FLAC__SUBSET_MAX_LPC_ORDER_48000HZ =(12)
+ FLAC__MIN_QLP_COEFF_PRECISION =(5)
+ FLAC__MAX_QLP_COEFF_PRECISION =(15)
+ FLAC__MAX_FIXED_ORDER =(4)
+ FLAC__MAX_RICE_PARTITION_ORDER =(15)
+ FLAC__SUBSET_MAX_RICE_PARTITION_ORDER =(8)
+
+ FLAC__VERSION_STRING = c_char_p.in_dll(libflac, "FLAC__VERSION_STRING")
+
+ FLAC__VENDOR_STRING = c_char_p.in_dll(libflac, "FLAC__VENDOR_STRING")
+
+ FLAC__STREAM_SYNC_STRING = (FLAC__byte * 4).in_dll(libflac, "FLAC__STREAM_SYNC_STRING")
+
+ FLAC__STREAM_SYNC = c_uint.in_dll(libflac, "FLAC__STREAM_SYNC")
+
+ FLAC__STREAM_SYNC_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_SYNC_LEN")
+
+ FLAC__STREAM_SYNC_LENGTH =(4)
+
+
+
+ FLAC__EntropyCodingMethodType = c_int
+
+ FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE = 0
+
+ FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2 = 1
+
+
+
+ libflac.FLAC__EntropyCodingMethodTypeString.restype = c_char_p
+ libflac.FLAC__EntropyCodingMethodTypeString.argtypes = []
+
+ def FLAC__EntropyCodingMethodTypeString():
+ return libflac.FLAC__EntropyCodingMethodTypeString()
+
+
+
+ class FLAC__EntropyCodingMethod_PartitionedRiceContents(Structure):
+ _fields_ = [("parameters", c_uint_p),
+ ("raw_bits", c_uint_p),
+ ("capacity_by_order", c_uint)]
+
+ class FLAC__EntropyCodingMethod_PartitionedRice(Structure):
+ _fields_ = [("order", c_uint),
+ ("contents", POINTER(FLAC__EntropyCodingMethod_PartitionedRiceContents))]
+
+
+ FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN = c_uint.in_dll(libflac, "FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN")
+
+ FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN = c_uint.in_dll(libflac, "FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN")
+
+ FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN = c_uint.in_dll(libflac, "FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN")
+
+ FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN = c_uint.in_dll(libflac, "FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN")
+
+ FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER = c_uint.in_dll(libflac, "FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER")
+
+
+ class FLAC__EntropyCodingMethod_data(Union):
+ _fields_ = [("partitioned_rice", FLAC__EntropyCodingMethod_PartitionedRice)]
+
+ class FLAC__EntropyCodingMethod(Structure):
+ _fields_ = [("type", POINTER(FLAC__EntropyCodingMethodType)),
+ ("data", FLAC__EntropyCodingMethod_data)]
+
+ FLAC__ENTROPY_CODING_METHOD_TYPE_LEN = c_uint.in_dll(libflac, "FLAC__ENTROPY_CODING_METHOD_TYPE_LEN")
+
+
+
+ FLAC__SubframeType = c_int
+ FLAC__SUBFRAME_TYPE_CONSTANT = 0
+ FLAC__SUBFRAME_TYPE_VERBATIM = 1
+ FLAC__SUBFRAME_TYPE_FIXED = 2
+ FLAC__SUBFRAME_TYPE_LPC = 3
+
+
+
+ libflac.FLAC__SubframeTypeString.restype = c_char_p
+ libflac.FLAC__SubframeTypeString.argtypes = []
+
+ def FLAC__SubframeTypeString():
+ return libflac.FLAC__SubframeTypeString()
+
+
+
+ class FLAC__Subframe_Constant(Structure):
+ _fields_ = [("value", FLAC__int32)]
+
+
+ class FLAC__Subframe_Verbatim(Structure):
+ _fields_ = [("data", FLAC__int32_p)]
+
+
+ class FLAC__Subframe_Fixed(Structure):
+ _fields_ = [("entropy_coding_method", FLAC__EntropyCodingMethod),
+ ("order", c_uint),
+ ("warmup", FLAC__int32 * FLAC__MAX_FIXED_ORDER),
+ ("residual", FLAC__int32_p)]
+
+
+ class FLAC__Subframe_LPC(Structure):
+ _fields_ = [("entropy_coding_method", FLAC__EntropyCodingMethod),
+ ("order", c_uint),
+ ("qlp_coeff_precision", c_uint),
+ ("quantization_level", c_int),
+ ("qlp_coeff", FLAC__int32 * FLAC__MAX_LPC_ORDER),
+ ("warmup", FLAC__int32 * FLAC__MAX_LPC_ORDER),
+ ("residual", FLAC__int32_p)]
+
+
+ FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN = c_uint.in_dll(libflac, "FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN")
+
+ FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN = c_uint.in_dll(libflac, "FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN")
+
+
+
+ class FLAC__Subframe_data(Union):
+ _fields_ = [("constant", FLAC__Subframe_Constant),
+ ("fixed", FLAC__Subframe_Fixed),
+ ("lpc", FLAC__Subframe_LPC),
+ ("verbatim", FLAC__Subframe_Verbatim)]
+
+ class FLAC__Subframe(Structure):
+ _fields_ = [("type", FLAC__SubframeType),
+ ("data", FLAC__Subframe_data),
+ ("wasted_bits", c_uint)]
+
+
+ FLAC__SUBFRAME_ZERO_PAD_LEN = c_uint.in_dll(libflac, "FLAC__SUBFRAME_ZERO_PAD_LEN")
+
+ FLAC__SUBFRAME_TYPE_LEN = c_uint.in_dll(libflac, "FLAC__SUBFRAME_TYPE_LEN")
+
+ FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN = c_uint.in_dll(libflac, "FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN")
+
+ FLAC__SUBFRAME_TYPE_CONSTANT_BYTE_ALIGNED_MASK = c_uint.in_dll(libflac, "FLAC__SUBFRAME_TYPE_CONSTANT_BYTE_ALIGNED_MASK")
+
+ FLAC__SUBFRAME_TYPE_VERBATIM_BYTE_ALIGNED_MASK = c_uint.in_dll(libflac, "FLAC__SUBFRAME_TYPE_VERBATIM_BYTE_ALIGNED_MASK")
+
+ FLAC__SUBFRAME_TYPE_FIXED_BYTE_ALIGNED_MASK = c_uint.in_dll(libflac, "FLAC__SUBFRAME_TYPE_FIXED_BYTE_ALIGNED_MASK")
+
+ FLAC__SUBFRAME_TYPE_LPC_BYTE_ALIGNED_MASK = c_uint.in_dll(libflac, "FLAC__SUBFRAME_TYPE_LPC_BYTE_ALIGNED_MASK")
+
+
+ FLAC__ChannelAssignment = c_int
+
+ FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT = 0
+ FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE = 1
+ FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE = 2
+ FLAC__CHANNEL_ASSIGNMENT_MID_SIDE = 3
+
+
+
+ libflac.FLAC__ChannelAssignmentString.restype = c_char_p
+ libflac.FLAC__ChannelAssignmentString.argtypes = []
+
+ def FLAC__ChannelAssignmentString():
+ return libflac.FLAC__ChannelAssignmentString()
+
+ FLAC__FrameNumberType = c_int
+
+
+ libflac.FLAC__FrameNumberTypeString.restype = c_char_p
+ libflac.FLAC__FrameNumberTypeString.argtypes = []
+
+ def FLAC__FrameNumberTypeString():
+ return libflac.FLAC__FrameNumberTypeString()
+
+
+ class FLAC__FrameHeader_number(Union):
+ _fields_ =[("frame_number", FLAC__uint32),
+ ("sample_number", FLAC__uint64)]
+
+ class FLAC__FrameHeader(Structure):
+ _fields_ = [("blocksize", c_uint),
+ ("sample_rate", c_uint),
+ ("channels", c_uint),
+ ("channel_assignment", FLAC__ChannelAssignment),
+ ("bits_per_sample", c_uint),
+ ("number_type", FLAC__FrameNumberType),
+ ("number", FLAC__FrameHeader_number),
+ ("crc", FLAC__uint8)]
+
+
+ FLAC__FRAME_HEADER_SYNC = c_uint.in_dll(libflac, "FLAC__FRAME_HEADER_SYNC")
+
+ FLAC__FRAME_HEADER_RESERVED_LEN = c_uint.in_dll(libflac, "FLAC__FRAME_HEADER_RESERVED_LEN")
+
+ FLAC__FRAME_HEADER_BLOCKING_STRATEGY_LEN = c_uint.in_dll(libflac, "FLAC__FRAME_HEADER_BLOCKING_STRATEGY_LEN")
+
+ FLAC__FRAME_HEADER_BLOCK_SIZE_LEN = c_uint.in_dll(libflac, "FLAC__FRAME_HEADER_BLOCK_SIZE_LEN")
+
+ FLAC__FRAME_HEADER_SAMPLE_RATE_LEN = c_uint.in_dll(libflac, "FLAC__FRAME_HEADER_SAMPLE_RATE_LEN")
+
+ FLAC__FRAME_HEADER_CHANNEL_ASSIGNMENT_LEN = c_uint.in_dll(libflac, "FLAC__FRAME_HEADER_CHANNEL_ASSIGNMENT_LEN")
+
+ FLAC__FRAME_HEADER_BITS_PER_SAMPLE_LEN = c_uint.in_dll(libflac, "FLAC__FRAME_HEADER_BITS_PER_SAMPLE_LEN")
+
+ FLAC__FRAME_HEADER_ZERO_PAD_LEN = c_uint.in_dll(libflac, "FLAC__FRAME_HEADER_ZERO_PAD_LEN")
+
+ FLAC__FRAME_HEADER_CRC_LEN = c_uint.in_dll(libflac, "FLAC__FRAME_HEADER_CRC_LEN")
+
+
+
+ class FLAC__FrameFooter(Structure):
+ _fields_ = [("crc", FLAC__uint16)]
+
+ FLAC__FRAME_FOOTER_CRC_LEN = c_uint.in_dll(libflac, "FLAC__FRAME_FOOTER_CRC_LEN")
+
+
+
+ class FLAC__Frame(Structure):
+ _fields_ = [("header", FLAC__FrameHeader),
+ ("subframes", FLAC__Subframe * FLAC__MAX_CHANNELS),
+ ("footer", FLAC__FrameFooter)]
+
+
+ FLAC__MetadataType = c_int
+
+ FLAC__METADATA_TYPE_STREAMINFO = 0
+
+ FLAC__METADATA_TYPE_PADDING = 1
+
+ FLAC__METADATA_TYPE_APPLICATION = 2
+
+ FLAC__METADATA_TYPE_SEEKTABLE = 3
+
+ FLAC__METADATA_TYPE_VORBIS_COMMENT = 4
+
+ FLAC__METADATA_TYPE_CUESHEET = 5
+
+ FLAC__METADATA_TYPE_PICTURE = 6
+
+ FLAC__METADATA_TYPE_UNDEFINED = 7
+
+ FLAC__MAX_METADATA_TYPE = FLAC__MAX_METADATA_TYPE_CODE
+
+
+
+ libflac.FLAC__MetadataTypeString.restype = c_char_p
+ libflac.FLAC__MetadataTypeString.argtypes = []
+
+ def FLAC__MetadataTypeString():
+ return libflac.FLAC__MetadataTypeString()
+
+
+
+ class FLAC__StreamMetadata_StreamInfo(Structure):
+ _fields_ = [("min_blocksize", c_uint),
+ ("max_framesize", c_uint),
+ ("min_framesize", c_uint),
+ ("max_framesize", c_uint),
+ ("sample_rate", c_uint),
+ ("channels", c_uint),
+ ("bits_per_sample", c_uint),
+ ("total_samples", FLAC__uint64),
+ ("md5sum", FLAC__byte*16)]
+
+ FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN")
+
+ FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN")
+
+ FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN")
+
+ FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN")
+
+ FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN")
+
+
+ FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN")
+
+ FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN")
+
+ FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN")
+
+ FLAC__STREAM_METADATA_STREAMINFO_MD5SUM_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_STREAMINFO_MD5SUM_LEN")
+
+ FLAC__STREAM_METADATA_STREAMINFO_LENGTH =(34)
+
+
+ class FLAC__StreamMetadata_Padding(Structure):
+ _fields_ = [("dummy", c_int)]
+
+
+
+ class FLAC__StreamMetadata_Application(Structure):
+ _fields_ = [("id", FLAC__byte*4),
+ ("data", FLAC__byte_p)]
+
+ FLAC__STREAM_METADATA_APPLICATION_ID_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_APPLICATION_ID_LEN")
+
+
+ class FLAC__StreamMetadata_SeekPoint(Structure):
+ _fields_ = [("sample_number", FLAC__uint64),
+ ("stream_offset", FLAC__uint64),
+ ("frame_samples", c_uint)]
+
+ FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN")
+
+ FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN")
+
+ FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN")
+
+ FLAC__STREAM_METADATA_SEEKPOINT_LENGTH =(18)
+
+
+ FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER = FLAC__uint64.in_dll(libflac, "FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER")
+
+ class FLAC__StreamMetadata_SeekTable(Structure):
+ _fields_ = [("num_points", c_uint),
+ ("points", POINTER(FLAC__StreamMetadata_SeekPoint))]
+
+ class FLAC__StreamMetadata_VorbisComment_Entry(Structure):
+ _fields_ = [("length", FLAC__uint32),
+ ("entry", FLAC__byte_p)]
+
+ FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN")
+
+
+ class FLAC__StreamMetadata_VorbisComment(Structure):
+ _fields_ = [("vendor_string", FLAC__StreamMetadata_VorbisComment_Entry),
+ ("num_comments", FLAC__uint32),
+ ("comments", POINTER(FLAC__StreamMetadata_VorbisComment_Entry))]
+
+ FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN")
+
+
+ class FLAC__StreamMetadata_CueSheet_Index(Structure):
+ _fields_ = [("offset", FLAC__uint64),
+ ("number", FLAC__byte)]
+
+
+ FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN")
+
+ FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN")
+
+ FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN")
+
+
+ class FLAC__StreamMetadata_CueSheet_Track(Structure):
+ _fields_ = [("offset", FLAC__uint64),
+ ("number", FLAC__byte),
+ ("isrc", c_char*13),
+ ("type", c_uint),
+ ("pre_emphasis", c_uint),
+ ("num_indices", FLAC__byte),
+ ("indices", POINTER(FLAC__StreamMetadata_CueSheet_Index))]
+
+ FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN")
+
+ FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN")
+
+ FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN")
+
+ FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN")
+
+ FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN")
+
+ FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN")
+
+ FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN")
+
+
+ class FLAC__StreamMetadata_CueSheet(Structure):
+ _fields_ = [("media_catalog_number", c_char*129),
+ ("lead_in", FLAC__uint64),
+ ("is_cd", FLAC__bool),
+ ("num_tracks", c_uint),
+ ("tracks", POINTER(FLAC__StreamMetadata_CueSheet_Track))]
+
+ FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN")
+
+
+ FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN")
+
+ FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN")
+
+ FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN")
+
+ FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN")
+
+
+ FLAC__StreamMetadata_Picture_Type = c_int
+ FLAC__STREAM_METADATA_PICTURE_TYPE_OTHER = 0
+ FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD = 1
+ FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON = 2
+ FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER = 3
+ FLAC__STREAM_METADATA_PICTURE_TYPE_BACK_COVER = 4
+ FLAC__STREAM_METADATA_PICTURE_TYPE_LEAFLET_PAGE = 5
+ FLAC__STREAM_METADATA_PICTURE_TYPE_MEDIA = 6
+ FLAC__STREAM_METADATA_PICTURE_TYPE_LEAD_ARTIST = 7
+ FLAC__STREAM_METADATA_PICTURE_TYPE_ARTIST = 8
+ FLAC__STREAM_METADATA_PICTURE_TYPE_CONDUCTOR = 9
+ FLAC__STREAM_METADATA_PICTURE_TYPE_BAND = 10
+ FLAC__STREAM_METADATA_PICTURE_TYPE_COMPOSER = 11
+ FLAC__STREAM_METADATA_PICTURE_TYPE_LYRICIST = 12
+ FLAC__STREAM_METADATA_PICTURE_TYPE_RECORDING_LOCATION = 13
+ FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_RECORDING = 14
+ FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_PERFORMANCE = 15
+ FLAC__STREAM_METADATA_PICTURE_TYPE_VIDEO_SCREEN_CAPTURE = 16
+ FLAC__STREAM_METADATA_PICTURE_TYPE_FISH = 17
+ FLAC__STREAM_METADATA_PICTURE_TYPE_ILLUSTRATION = 18
+ FLAC__STREAM_METADATA_PICTURE_TYPE_BAND_LOGOTYPE = 19
+ FLAC__STREAM_METADATA_PICTURE_TYPE_PUBLISHER_LOGOTYPE = 20
+
+
+ libflac.FLAC__StreamMetadata_Picture_TypeString.restype = c_char_p
+ libflac.FLAC__StreamMetadata_Picture_TypeString.argtypes = []
+
+ def FLAC__StreamMetadata_Picture_TypeString():
+ return libflac.FLAC__StreamMetadata_Picture_TypeString()
+
+
+ class FLAC__StreamMetadata_Picture(Structure):
+ _fields_ = [("type", FLAC__StreamMetadata_Picture_Type),
+ ("mime_type", c_char_p),
+ ("description", FLAC__byte_p),
+ ("width", FLAC__uint32),
+ ("height", FLAC__uint32),
+ ("depth", FLAC__uint32),
+ ("colors", FLAC__uint32),
+ ("data_length", FLAC__uint32),
+ ("data", FLAC__byte)]
+
+ FLAC__STREAM_METADATA_PICTURE_TYPE_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_PICTURE_TYPE_LEN")
+
+ FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN")
+
+ FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN")
+
+ FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN")
+
+ FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN")
+
+
+ FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN")
+
+ FLAC__STREAM_METADATA_PICTURE_COLORS_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_PICTURE_COLORS_LEN")
+
+ FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN")
+
+
+ class FLAC__StreamMetadata_Unknown(Structure):
+ _fields_ = [("data", FLAC__byte_p)]
+
+
+ class FLAC__StreamMetadata_data(Union):
+ _fields_ = [("stream_info", FLAC__StreamMetadata_StreamInfo),
+ ("padding", FLAC__StreamMetadata_Padding),
+ ("application", FLAC__StreamMetadata_Application),
+ ("seek_table", FLAC__StreamMetadata_SeekTable),
+ ("vorbis_comment", FLAC__StreamMetadata_VorbisComment),
+ ("cue_sheet", FLAC__StreamMetadata_CueSheet),
+ ("picture", FLAC__StreamMetadata_Picture),
+ ("unknown", FLAC__StreamMetadata_Unknown)]
+
+ class FLAC__StreamMetadata(Structure):
+ _fields_ = [("type", FLAC__MetadataType),
+ ("is_last", FLAC__bool),
+ ("length", c_uint),
+ ("data", FLAC__StreamMetadata_data)]
+
+ FLAC__STREAM_METADATA_IS_LAST_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_IS_LAST_LEN")
+
+ FLAC__STREAM_METADATA_TYPE_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_TYPE_LEN")
+
+ FLAC__STREAM_METADATA_LENGTH_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_LENGTH_LEN")
+
+ FLAC__STREAM_METADATA_HEADER_LENGTH =(4)
+
+
+
+ libflac.FLAC__format_sample_rate_is_valid.restype = FLAC__bool
+ libflac.FLAC__format_sample_rate_is_valid.argtypes = [c_uint]
+
+ def FLAC__format_sample_rate_is_valid(sample_rate):
+ return libflac.FLAC__format_sample_rate_is_valid(sample_rate)
+
+
+ libflac.FLAC__format_blocksize_is_subset.restype = FLAC__bool
+ libflac.FLAC__format_blocksize_is_subset.argtypes = [c_uint, c_uint]
+
+ def FLAC__format_blocksize_is_subset(blocksize, sample_rate):
+ return libflac.FLAC__format_blocksize_is_subset(blocksize, sample_rate)
+
+
+ libflac.FLAC__format_sample_rate_is_subset.restype = FLAC__bool
+ libflac.FLAC__format_sample_rate_is_subset.argtypes = [c_uint]
+
+ def FLAC__format_sample_rate_is_subset(sample_rate):
+ return libflac.FLAC__format_sample_rate_is_subset(sample_rate)
+
+
+ libflac.FLAC__format_vorbiscomment_entry_name_is_legal.restype = FLAC__bool
+ libflac.FLAC__format_vorbiscomment_entry_name_is_legal.argtypes = [c_char_p]
+
+ def FLAC__format_vorbiscomment_entry_name_is_legal(name):
+ return libflac.FLAC__format_vorbiscomment_entry_name_is_legal(name)
+
+ libflac.FLAC__format_vorbiscomment_entry_value_is_legal.restype = FLAC__bool
+ libflac.FLAC__format_vorbiscomment_entry_value_is_legal.argtypes = [FLAC__byte_p, c_uint]
+
+ def FLAC__format_vorbiscomment_entry_value_is_legal(value, length):
+ return libflac.FLAC__format_vorbiscomment_entry_value_is_legal(value, length)
+
+ libflac.FLAC__format_vorbiscomment_entry_is_legal.restype = FLAC__bool
+ libflac.FLAC__format_vorbiscomment_entry_is_legal.argtypes = [FLAC__byte_p, c_uint]
+
+ def FLAC__format_vorbiscomment_entry_is_legal(entry, length):
+ return libflac.FLAC__format_vorbiscomment_entry_is_legal(entry, length)
+
+ libflac.FLAC__format_seektable_is_legal.restype = FLAC__bool
+ libflac.FLAC__format_seektable_is_legal.argtypes = [POINTER(FLAC__StreamMetadata_SeekTable)]
+
+ def FLAC__format_seektable_is_legal(seek_table):
+ return libflac.FLAC__format_seektable_is_legal(seek_table)
+
+
+ libflac.FLAC__format_seektable_sort.restype = FLAC__bool
+ libflac.FLAC__format_seektable_sort.argtypes = [POINTER(FLAC__StreamMetadata_SeekTable)]
+
+ def FLAC__format_seektable_sort(seek_table):
+ return libflac.FLAC__format_seektable_sort(seek_table)
+
+ libflac.FLAC__format_cuesheet_is_legal.restype = FLAC__bool
+ libflac.FLAC__format_cuesheet_is_legal.argtypes = [POINTER(FLAC__StreamMetadata_CueSheet), FLAC__bool, c_char_p_p]
+
+ def FLAC__format_cuesheet_is_legal(cue_sheet, check_cd_da_subset, violation):
+ return libflac.FLAC__format_cuesheet_is_legal(cue_sheet, check_cd_da_subset, violation)
+
+ # /format
+
+ # metadata
+
+ libflac.FLAC__metadata_get_streaminfo.restype = FLAC__bool
+ libflac.FLAC__metadata_get_streaminfo.argtypes = [c_char_p, POINTER(FLAC__StreamMetadata)]
+
+ def FLAC__metadata_get_streaminfo(filename, streaminfo):
+ return libflac.FLAC__metadata_get_streaminfo(filename, streaminfo)
+
+ libflac.FLAC__metadata_get_tags.restype = FLAC__bool
+ libflac.FLAC__metadata_get_tags.argtypes = [c_char_p, POINTER(POINTER(FLAC__StreamMetadata))]
+
+ def FLAC__metadata_get_tags(filename, tags):
+ return libflac.FLAC__metadata_get_tags(filename, tags)
+
+ libflac.FLAC__metadata_get_cuesheet.restype = FLAC__bool
+ libflac.FLAC__metadata_get_cuesheet.argtypes = [c_char_p, POINTER(POINTER(FLAC__StreamMetadata))]
+
+ def FLAC__metadata_get_cuesheet(filename, cuesheet):
+ return libflac.FLAC__metadata_get_cuesheet(filename, cuesheet)
+
+ libflac.FLAC__metadata_get_picture.restype = FLAC__bool
+ libflac.FLAC__metadata_get_picture.argtypes = [c_char_p, POINTER(POINTER(FLAC__StreamMetadata)), FLAC__StreamMetadata_Picture_Type, c_char_p, FLAC__byte_p, c_uint, c_uint, c_uint, c_uint]
+
+ def FLAC__metadata_get_picture(filename, picture, type, mime_type, description, max_width, max_height, max_depth, max_colors):
+ return libflac.FLAC__metadata_get_picture(filename, picture, type, mime_type, description, max_width, max_height, max_depth, max_colors)
+
+
+ class FLAC__Metadata_SimpleIterator(Structure):
+ _fields_ = [("dummy", c_int)]
+
+ FLAC__Metadata_SimpleIteratorStatus = c_int
+
+ FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK = 0
+
+
+ libflac.FLAC__Metadata_SimpleIteratorStatusString.restype = c_char_p
+ libflac.FLAC__Metadata_SimpleIteratorStatusString.argtypes = []
+
+ def FLAC__Metadata_SimpleIteratorStatusString():
+ return libflac.FLAC__Metadata_SimpleIteratorStatusString()
+
+
+ libflac.FLAC__metadata_simple_iterator_new.restype = POINTER(FLAC__Metadata_SimpleIterator)
+ libflac.FLAC__metadata_simple_iterator_new.argtypes = []
+
+ def FLAC__metadata_simple_iterator_new():
+ return libflac.FLAC__metadata_simple_iterator_new()
+
+
+ libflac.FLAC__metadata_simple_iterator_delete.restype = None
+ libflac.FLAC__metadata_simple_iterator_delete.argtypes = [POINTER(FLAC__Metadata_SimpleIterator)]
+
+ def FLAC__metadata_simple_iterator_delete(iterator):
+ return libflac.FLAC__metadata_simple_iterator_delete(iterator)
+
+
+ libflac.FLAC__metadata_simple_iterator_status.restype = FLAC__Metadata_SimpleIteratorStatus
+ libflac.FLAC__metadata_simple_iterator_status.argtypes = [POINTER(FLAC__Metadata_SimpleIterator)]
+
+ def FLAC__metadata_simple_iterator_status(iterator):
+ return libflac.FLAC__metadata_simple_iterator_status(iterator)
+
+ libflac.FLAC__metadata_simple_iterator_init.restype = FLAC__bool
+ libflac.FLAC__metadata_simple_iterator_init.argtypes = [POINTER(FLAC__Metadata_SimpleIterator), c_char_p, FLAC__bool, FLAC__bool]
+
+ def FLAC__metadata_simple_iterator_init(iterator, filename, read_only, preserve_file_stats):
+ return libflac.FLAC__metadata_simple_iterator_init(iterator, filename, read_only, preserve_file_stats)
+
+ libflac.FLAC__metadata_simple_iterator_is_writable.restype = FLAC__bool
+ libflac.FLAC__metadata_simple_iterator_is_writable.argtypes = [POINTER(FLAC__Metadata_SimpleIterator)]
+
+ def FLAC__metadata_simple_iterator_is_writable(iterator):
+ return libflac.FLAC__metadata_simple_iterator_is_writable(iterator)
+
+ libflac.FLAC__metadata_simple_iterator_next.restype = FLAC__bool
+ libflac.FLAC__metadata_simple_iterator_next.argtypes = [POINTER(FLAC__Metadata_SimpleIterator)]
+
+ def FLAC__metadata_simple_iterator_next(iterator):
+ return libflac.FLAC__metadata_simple_iterator_next(iterator)
+
+ libflac.FLAC__metadata_simple_iterator_prev.restype = FLAC__bool
+ libflac.FLAC__metadata_simple_iterator_prev.argtypes = [POINTER(FLAC__Metadata_SimpleIterator)]
+
+ def FLAC__metadata_simple_iterator_prev(iterator):
+ return libflac.FLAC__metadata_simple_iterator_prev(iterator)
+
+ libflac.FLAC__metadata_simple_iterator_is_last.restype = FLAC__bool
+ libflac.FLAC__metadata_simple_iterator_is_last.argtypes = [POINTER(FLAC__Metadata_SimpleIterator)]
+
+ def FLAC__metadata_simple_iterator_is_last(iterator):
+ return libflac.FLAC__metadata_simple_iterator_is_last(iterator)
+
+ libflac.FLAC__metadata_simple_iterator_get_block_offset.restype = c_off_t
+ libflac.FLAC__metadata_simple_iterator_get_block_offset.argtypes = [POINTER(FLAC__Metadata_SimpleIterator)]
+
+ def FLAC__metadata_simple_iterator_get_block_offset(iterator):
+ return libflac.FLAC__metadata_simple_iterator_get_block_offset(iterator)
+
+ libflac.FLAC__metadata_simple_iterator_get_block_type.restype = FLAC__MetadataType
+ libflac.FLAC__metadata_simple_iterator_get_block_type.argtypes = [POINTER(FLAC__Metadata_SimpleIterator)]
+
+ def FLAC__metadata_simple_iterator_get_block_type(iterator):
+ return libflac.FLAC__metadata_simple_iterator_get_block_type(iterator)
+
+ libflac.FLAC__metadata_simple_iterator_get_block_length.restype = c_uint
+ libflac.FLAC__metadata_simple_iterator_get_block_length.argtypes = [POINTER(FLAC__Metadata_SimpleIterator)]
+
+ def FLAC__metadata_simple_iterator_get_block_length(iterator):
+ return libflac.FLAC__metadata_simple_iterator_get_block_length(iterator)
+
+ libflac.FLAC__metadata_simple_iterator_get_application_id.restype = FLAC__bool
+ libflac.FLAC__metadata_simple_iterator_get_application_id.argtypes = [POINTER(FLAC__Metadata_SimpleIterator), FLAC__byte_p]
+
+ def FLAC__metadata_simple_iterator_get_application_id(iterator, id):
+ return libflac.FLAC__metadata_simple_iterator_get_application_id(iterator, id)
+
+ libflac.FLAC__metadata_simple_iterator_get_block.restype = POINTER(FLAC__StreamMetadata)
+ libflac.FLAC__metadata_simple_iterator_get_block.argtypes = [POINTER(FLAC__Metadata_SimpleIterator)]
+
+ def FLAC__metadata_simple_iterator_get_block(iterator):
+ return libflac.FLAC__metadata_simple_iterator_get_block(iterator)
+
+ libflac.FLAC__metadata_simple_iterator_set_block.restype = FLAC__bool
+ libflac.FLAC__metadata_simple_iterator_set_block.argtypes = [POINTER(FLAC__Metadata_SimpleIterator), POINTER(FLAC__StreamMetadata), FLAC__bool]
+
+ def FLAC__metadata_simple_iterator_set_block(iterator, block, use_padding):
+ return libflac.FLAC__metadata_simple_iterator_set_block(iterator, block, use_padding)
+
+ libflac.FLAC__metadata_simple_iterator_insert_block_after.restype = FLAC__bool
+ libflac.FLAC__metadata_simple_iterator_insert_block_after.argtypes = [POINTER(FLAC__Metadata_SimpleIterator), POINTER(FLAC__StreamMetadata), FLAC__bool]
+
+ def FLAC__metadata_simple_iterator_insert_block_after(iterator, block, use_padding):
+ return libflac.FLAC__metadata_simple_iterator_insert_block_after(iterator, block, use_padding)
+
+ libflac.FLAC__metadata_simple_iterator_delete_block.restype = FLAC__bool
+ libflac.FLAC__metadata_simple_iterator_delete_block.argtypes = [POINTER(FLAC__Metadata_SimpleIterator), FLAC__bool]
+
+ def FLAC__metadata_simple_iterator_delete_block(iterator, use_padding):
+ return libflac.FLAC__metadata_simple_iterator_delete_block(iterator, use_padding)
+
+ class FLAC__Metadata_Chain(Structure):
+ _fields_ = [("dummy", c_int)]
+
+ class FLAC__Metadata_Iterator(Structure):
+ _fields_ = [("dummy", c_int)]
+
+ FLAC__Metadata_ChainStatus = c_int
+
+ FLAC__METADATA_CHAIN_STATUS_OK = 0
+
+ libflac.FLAC__Metadata_ChainStatusString.restype = c_char_p
+ libflac.FLAC__Metadata_ChainStatusString.argtypes = []
+
+ def FLAC__Metadata_ChainStatusString():
+ return libflac.FLAC__Metadata_ChainStatusString()
+
+ libflac.FLAC__metadata_chain_new.restype = POINTER(FLAC__Metadata_Chain)
+ libflac.FLAC__metadata_chain_new.argtypes = []
+
+ def FLAC__metadata_chain_new():
+ return libflac.FLAC__metadata_chain_new()
+
+ libflac.FLAC__metadata_chain_delete.restype = None
+ libflac.FLAC__metadata_chain_delete.argtypes = [POINTER(FLAC__Metadata_Chain)]
+
+ def FLAC__metadata_chain_delete(chain):
+ return libflac.FLAC__metadata_chain_delete(chain)
+
+ libflac.FLAC__metadata_chain_status.restype = FLAC__Metadata_ChainStatus
+ libflac.FLAC__metadata_chain_status.argtypes = [POINTER(FLAC__Metadata_Chain)]
+
+ def FLAC__metadata_chain_status(chain):
+ return libflac.FLAC__metadata_chain_status(chain)
+
+ libflac.FLAC__metadata_chain_read.restype = FLAC__bool
+ libflac.FLAC__metadata_chain_read.argtypes = [POINTER(FLAC__Metadata_Chain), c_char_p]
+
+ def FLAC__metadata_chain_read(chain, filename):
+ return libflac.FLAC__metadata_chain_read(chain, filename)
+
+ libflac.FLAC__metadata_chain_read_ogg.restype = FLAC__bool
+ libflac.FLAC__metadata_chain_read_ogg.argtypes = [POINTER(FLAC__Metadata_Chain), c_char_p]
+
+ def FLAC__metadata_chain_read_ogg(chain, filename):
+ return libflac.FLAC__metadata_chain_read_ogg(chain, filename)
+
+ libflac.FLAC__metadata_chain_read_with_callbacks.restype = FLAC__bool
+ libflac.FLAC__metadata_chain_read_with_callbacks.argtypes = [POINTER(FLAC__Metadata_Chain), FLAC__IOHandle, FLAC__IOCallbacks]
+
+ def FLAC__metadata_chain_read_with_callbacks(chain, handle, callbacks):
+ return libflac.FLAC__metadata_chain_read_with_callbacks(chain, handle, callbacks)
+
+ libflac.FLAC__metadata_chain_read_ogg_with_callbacks.restype = FLAC__bool
+ libflac.FLAC__metadata_chain_read_ogg_with_callbacks.argtypes = [POINTER(FLAC__Metadata_Chain), FLAC__IOHandle, FLAC__IOCallbacks]
+
+ def FLAC__metadata_chain_read_ogg_with_callbacks(chain, handle, callbacks):
+ return libflac.FLAC__metadata_chain_read_ogg_with_callbacks(chain, handle, callbacks)
+
+ libflac.FLAC__metadata_chain_check_if_tempfile_needed.restype = FLAC__bool
+ libflac.FLAC__metadata_chain_check_if_tempfile_needed.argtypes = [POINTER(FLAC__Metadata_Chain), FLAC__bool]
+
+ def FLAC__metadata_chain_check_if_tempfile_needed(chain, use_padding):
+ return libflac.FLAC__metadata_chain_check_if_tempfile_needed(chain, use_padding)
+
+ libflac.FLAC__metadata_chain_write.restype = FLAC__bool
+ libflac.FLAC__metadata_chain_write.argtypes = [POINTER(FLAC__Metadata_Chain), FLAC__bool, FLAC__bool]
+
+ def FLAC__metadata_chain_write(chain, use_padding, preserve_file_stats):
+ return libflac.FLAC__metadata_chain_write(chain, use_padding, preserve_file_stats)
+
+ libflac.FLAC__metadata_chain_write_with_callbacks.restype = FLAC__bool
+ libflac.FLAC__metadata_chain_write_with_callbacks.argtypes = [POINTER(FLAC__Metadata_Chain), FLAC__bool, FLAC__IOHandle, FLAC__IOCallbacks]
+
+ def FLAC__metadata_chain_write_with_callbacks(chain, use_padding, handle, callbacks):
+ return libflac.FLAC__metadata_chain_write_with_callbacks(chain, use_padding, handle, callbacks)
+
+ libflac.FLAC__metadata_chain_write_with_callbacks_and_tempfile.restype = FLAC__bool
+ libflac.FLAC__metadata_chain_write_with_callbacks_and_tempfile.argtypes = [POINTER(FLAC__Metadata_Chain), FLAC__bool, FLAC__IOHandle, FLAC__IOCallbacks, FLAC__IOHandle, FLAC__IOCallbacks]
+
+ def FLAC__metadata_chain_write_with_callbacks_and_tempfile(chain, use_padding, handle, callbacks, temp_handle, temp_callbacks):
+ return libflac.FLAC__metadata_chain_write_with_callbacks_and_tempfile(chain, use_padding, handle, callbacks, temp_handle, temp_callbacks)
+
+ libflac.FLAC__metadata_chain_merge_padding.restype = None
+ libflac.FLAC__metadata_chain_merge_padding.argtypes = [POINTER(FLAC__Metadata_Chain)]
+
+ def FLAC__metadata_chain_merge_padding(chain):
+ return libflac.FLAC__metadata_chain_merge_padding(chain)
+
+ libflac.FLAC__metadata_chain_sort_padding.restype = None
+ libflac.FLAC__metadata_chain_sort_padding.argtypes = [POINTER(FLAC__Metadata_Chain)]
+
+ def FLAC__metadata_chain_sort_padding(chain):
+ return libflac.FLAC__metadata_chain_sort_padding(chain)
+
+ libflac.FLAC__metadata_iterator_new.restype = POINTER(FLAC__Metadata_Iterator)
+ libflac.FLAC__metadata_iterator_new.argtypes = []
+
+ def FLAC__metadata_iterator_new():
+ return libflac.FLAC__metadata_iterator_new()
+
+ libflac.FLAC__metadata_iterator_delete.restype = None
+ libflac.FLAC__metadata_iterator_delete.argtypes = [POINTER(FLAC__Metadata_Iterator)]
+
+ def FLAC__metadata_iterator_delete(iterator):
+ return libflac.FLAC__metadata_iterator_delete(iterator)
+
+ libflac.FLAC__metadata_iterator_init.restype = None
+ libflac.FLAC__metadata_iterator_init.argtypes = [POINTER(FLAC__Metadata_Iterator), POINTER(FLAC__Metadata_Chain)]
+
+ def FLAC__metadata_iterator_init(iterator, chain):
+ return libflac.FLAC__metadata_iterator_init(iterator, chain)
+
+ libflac.FLAC__metadata_iterator_next.restype = FLAC__bool
+ libflac.FLAC__metadata_iterator_next.argtypes = [POINTER(FLAC__Metadata_Iterator)]
+
+ def FLAC__metadata_iterator_next(iterator):
+ return libflac.FLAC__metadata_iterator_next(iterator)
+
+ libflac.FLAC__metadata_iterator_prev.restype = FLAC__bool
+ libflac.FLAC__metadata_iterator_prev.argtypes = [POINTER(FLAC__Metadata_Iterator)]
+
+ def FLAC__metadata_iterator_prev(iterator):
+ return libflac.FLAC__metadata_iterator_prev(iterator)
+
+ libflac.FLAC__metadata_iterator_get_block_type.restype = FLAC__MetadataType
+ libflac.FLAC__metadata_iterator_get_block_type.argtypes = [POINTER(FLAC__Metadata_Iterator)]
+
+ def FLAC__metadata_iterator_get_block_type(iterator):
+ return libflac.FLAC__metadata_iterator_get_block_type(iterator)
+
+ libflac.FLAC__metadata_iterator_get_block_type.restype = POINTER(FLAC__StreamMetadata)
+ libflac.FLAC__metadata_iterator_get_block_type.argtypes = [POINTER(FLAC__Metadata_Iterator)]
+
+ def FLAC__metadata_iterator_get_block_type(iterator):
+ return libflac.FLAC__metadata_iterator_get_block_type(iterator)
+
+ libflac.FLAC__metadata_iterator_set_block.restype = FLAC__bool
+ libflac.FLAC__metadata_iterator_set_block.argtypes = [POINTER(FLAC__Metadata_Iterator), POINTER(FLAC__StreamMetadata)]
+
+ def FLAC__metadata_iterator_set_block(iterator, block):
+ return libflac.FLAC__metadata_iterator_set_block(iterator, block)
+
+ libflac.FLAC__metadata_iterator_delete_block.restype = FLAC__bool
+ libflac.FLAC__metadata_iterator_delete_block.argtypes = [POINTER(FLAC__Metadata_Iterator), FLAC__bool]
+
+ def FLAC__metadata_iterator_delete_block(iterator, replace_with_padding):
+ return libflac.FLAC__metadata_iterator_delete_block(iterator, replace_with_padding)
+
+ libflac.FLAC__metadata_iterator_insert_block_before.restype = FLAC__bool
+ libflac.FLAC__metadata_iterator_insert_block_before.argtypes = [POINTER(FLAC__Metadata_Iterator), POINTER(FLAC__StreamMetadata)]
+
+ def FLAC__metadata_iterator_insert_block_before(iterator, block):
+ return libflac.FLAC__metadata_iterator_insert_block_before(iterator, block)
+
+ libflac.FLAC__metadata_iterator_insert_block_after.restype = FLAC__bool
+ libflac.FLAC__metadata_iterator_insert_block_after.argtypes = [POINTER(FLAC__Metadata_Iterator), POINTER(FLAC__StreamMetadata)]
+
+ def FLAC__metadata_iterator_insert_block_after(iterator, block):
+ return libflac.FLAC__metadata_iterator_insert_block_after(iterator, block)
+
+ libflac.FLAC__metadata_object_new.restype = POINTER(FLAC__StreamMetadata)
+ libflac.FLAC__metadata_object_new.argtypes = [POINTER(FLAC__MetadataType)]
+
+ def FLAC__metadata_object_new(type):
+ return libflac.FLAC__metadata_object_new(type)
+
+ libflac.FLAC__metadata_object_clone.restype = POINTER(FLAC__StreamMetadata)
+ libflac.FLAC__metadata_object_clone.argtypes = [POINTER(FLAC__StreamMetadata)]
+
+ def FLAC__metadata_object_clone(object):
+ return libflac.FLAC__metadata_object_clone(object)
+
+ libflac.FLAC__metadata_object_delete.restype = None
+ libflac.FLAC__metadata_object_delete.argtypes = [POINTER(FLAC__StreamMetadata)]
+
+ def FLAC__metadata_object_delete(object):
+ return libflac.FLAC__metadata_object_delete(object)
+
+ libflac.FLAC__metadata_object_is_equal.restype = FLAC__bool
+ libflac.FLAC__metadata_object_is_equal.argtypes = [POINTER(FLAC__StreamMetadata), POINTER(FLAC__StreamMetadata)]
+
+ def FLAC__metadata_object_is_equal(block1, block2):
+ return libflac.FLAC__metadata_object_is_equal(block1, block2)
+
+ libflac.FLAC__metadata_object_application_set_data.restype = FLAC__bool
+ libflac.FLAC__metadata_object_application_set_data.argtypes = [POINTER(FLAC__StreamMetadata), FLAC__byte_p, c_uint, FLAC__bool]
+
+ def FLAC__metadata_object_application_set_data(object, data, length, copy):
+ return libflac.FLAC__metadata_object_application_set_data(object, data, length, copy)
+
+ libflac.FLAC__metadata_object_seektable_resize_points.restype = FLAC__bool
+ libflac.FLAC__metadata_object_seektable_resize_points.argtypes = [POINTER(FLAC__StreamMetadata),c_uint]
+
+ def FLAC__metadata_object_seektable_resize_points(object, new_num_points):
+ return libflac.FLAC__metadata_object_seektable_resize_points(object, new_num_points)
+
+ libflac.FLAC__metadata_object_seektable_set_point.restype = None
+ libflac.FLAC__metadata_object_seektable_set_point.argtypes = [POINTER(FLAC__StreamMetadata),c_uint, FLAC__StreamMetadata_SeekPoint]
+
+ def FLAC__metadata_object_seektable_set_point(object, point_num, point):
+ return libflac.FLAC__metadata_object_seektable_set_point(object, point_num, point)
+
+ libflac.FLAC__metadata_object_seektable_insert_point.restype = FLAC__bool
+ libflac.FLAC__metadata_object_seektable_insert_point.argtypes = [POINTER(FLAC__StreamMetadata),c_uint, FLAC__StreamMetadata_SeekPoint]
+
+ def FLAC__metadata_object_seektable_insert_point(object, point_num, point):
+ return libflac.FLAC__metadata_object_seektable_insert_point(object, point_num, point)
+
+ libflac.FLAC__metadata_object_seektable_delete_point.restype = FLAC__bool
+ libflac.FLAC__metadata_object_seektable_delete_point.argtypes = [POINTER(FLAC__StreamMetadata),c_uint]
+
+ def FLAC__metadata_object_seektable_delete_point(object, point_num):
+ return libflac.FLAC__metadata_object_seektable_delete_point(object, point_num)
+
+ libflac.FLAC__metadata_object_seektable_is_legal.restype = FLAC__bool
+ libflac.FLAC__metadata_object_seektable_is_legal.argtypes = [POINTER(FLAC__StreamMetadata)]
+
+ def FLAC__metadata_object_seektable_is_legal(object):
+ return libflac.FLAC__metadata_object_seektable_is_legal(object)
+
+ libflac.FLAC__metadata_object_seektable_template_append_placeholders.restype = FLAC__bool
+ libflac.FLAC__metadata_object_seektable_template_append_placeholders.argtypes = [POINTER(FLAC__StreamMetadata), c_uint]
+
+ def FLAC__metadata_object_seektable_template_append_placeholders(object, num):
+ return libflac.FLAC__metadata_object_seektable_template_append_placeholders(object, num)
+
+ libflac.FLAC__metadata_object_seektable_template_append_point.restype = FLAC__bool
+ libflac.FLAC__metadata_object_seektable_template_append_point.argtypes = [POINTER(FLAC__StreamMetadata), FLAC__uint64]
+
+ def FLAC__metadata_object_seektable_template_append_point(object, sample_number):
+ return libflac.FLAC__metadata_object_seektable_template_append_point(object, sample_number)
+
+ libflac.FLAC__metadata_object_seektable_template_append_points.restype = FLAC__bool
+ libflac.FLAC__metadata_object_seektable_template_append_points.argtypes = [POINTER(FLAC__StreamMetadata), POINTER(FLAC__uint64*0), c_uint]
+
+ def FLAC__metadata_object_seektable_template_append_points(object, sample_numbers, num):
+ return libflac.FLAC__metadata_object_seektable_template_append_points(object, sample_numbers, num)
+
+ libflac.FLAC__metadata_object_seektable_template_append_spaced_points.restype = FLAC__bool
+ libflac.FLAC__metadata_object_seektable_template_append_spaced_points.argtypes = [POINTER(FLAC__StreamMetadata), c_uint, FLAC__uint64]
+
+ def FLAC__metadata_object_seektable_template_append_spaced_points(object, num, total_samples):
+ return libflac.FLAC__metadata_object_seektable_template_append_spaced_points(object, num, total_samples)
+
+ libflac.FLAC__metadata_object_seektable_template_append_spaced_points_by_samples.restype = FLAC__bool
+ libflac.FLAC__metadata_object_seektable_template_append_spaced_points_by_samples.argtypes = [POINTER(FLAC__StreamMetadata), c_uint, FLAC__uint64]
+
+ def FLAC__metadata_object_seektable_template_append_spaced_points_by_samples(object, samples, total_samples):
+ return libflac.FLAC__metadata_object_seektable_template_append_spaced_points_by_samples(object, samples, total_samples)
+
+ libflac.FLAC__metadata_object_seektable_template_sort.restype = FLAC__bool
+ libflac.FLAC__metadata_object_seektable_template_sort.argtypes = [POINTER(FLAC__StreamMetadata), FLAC__bool]
+
+ def FLAC__metadata_object_seektable_template_sort(object, compact):
+ return libflac.FLAC__metadata_object_seektable_template_sort(object, compact)
+
+ libflac.FLAC__metadata_object_vorbiscomment_set_vendor_string.restype = FLAC__bool
+ libflac.FLAC__metadata_object_vorbiscomment_set_vendor_string.argtypes = [POINTER(FLAC__StreamMetadata), FLAC__StreamMetadata_VorbisComment_Entry, FLAC__bool]
+
+ def FLAC__metadata_object_vorbiscomment_set_vendor_string(object, entry, copy):
+ return libflac.FLAC__metadata_object_vorbiscomment_set_vendor_string(object, entry, copy)
+
+ libflac.FLAC__metadata_object_vorbiscomment_resize_comments.restype = FLAC__bool
+ libflac.FLAC__metadata_object_vorbiscomment_resize_comments.argtypes = [POINTER(FLAC__StreamMetadata), c_uint]
+
+ def FLAC__metadata_object_vorbiscomment_resize_comments(object, new_num_comments):
+ return libflac.FLAC__metadata_object_vorbiscomment_resize_comments(object, new_num_comments)
+
+ libflac.FLAC__metadata_object_vorbiscomment_set_comment.restype = FLAC__bool
+ libflac.FLAC__metadata_object_vorbiscomment_set_comment.argtypes = [POINTER(FLAC__StreamMetadata), c_uint, FLAC__StreamMetadata_VorbisComment_Entry, FLAC__bool]
+
+ def FLAC__metadata_object_vorbiscomment_set_comment(object, comment_num, entry, copy):
+ return libflac.FLAC__metadata_object_vorbiscomment_set_comment(object, comment_num, entry, copy)
+
+ libflac.FLAC__metadata_object_vorbiscomment_insert_comment.restype = FLAC__bool
+ libflac.FLAC__metadata_object_vorbiscomment_insert_comment.argtypes = [POINTER(FLAC__StreamMetadata), c_uint, FLAC__StreamMetadata_VorbisComment_Entry, FLAC__bool]
+
+ def FLAC__metadata_object_vorbiscomment_insert_comment(object, comment_num, entry, copy):
+ return libflac.FLAC__metadata_object_vorbiscomment_insert_comment(object, comment_num, entry, copy)
+
+ libflac.FLAC__metadata_object_vorbiscomment_append_comment.restype = FLAC__bool
+ libflac.FLAC__metadata_object_vorbiscomment_append_comment.argtypes = [POINTER(FLAC__StreamMetadata), c_uint, FLAC__StreamMetadata_VorbisComment_Entry, FLAC__bool]
+
+ def FLAC__metadata_object_vorbiscomment_append_comment(object, entry, copy):
+ return libflac.FLAC__metadata_object_vorbiscomment_append_comment(object,entry, copy)
+
+ libflac.FLAC__metadata_object_vorbiscomment_replace_comment.restype = FLAC__bool
+ libflac.FLAC__metadata_object_vorbiscomment_replace_comment.argtypes = [POINTER(FLAC__StreamMetadata), c_uint, FLAC__StreamMetadata_VorbisComment_Entry, FLAC__bool, FLAC__bool]
+
+ def FLAC__metadata_object_vorbiscomment_replace_comment(object, entry, all, copy):
+ return libflac.FLAC__metadata_object_vorbiscomment_replace_comment(object,entry, all, copy)
+
+ libflac.FLAC__metadata_object_vorbiscomment_delete_comment.restype = FLAC__bool
+ libflac.FLAC__metadata_object_vorbiscomment_delete_comment.argtypes = [POINTER(FLAC__StreamMetadata), c_uint]
+
+ def FLAC__metadata_object_vorbiscomment_delete_comment(object, comment_num):
+ return libflac.FLAC__metadata_object_vorbiscomment_delete_comment(object,comment_num)
+
+ libflac.FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair.restype = FLAC__bool
+ libflac.FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair.argtypes = [POINTER(FLAC__StreamMetadata_VorbisComment_Entry), c_char_p, c_char_p]
+
+ def FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(entry, field_name, field_value):
+ return libflac.FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(entry, field_name, field_value)
+
+ libflac.FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair.restype = FLAC__bool
+ libflac.FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair.argtypes = [POINTER(FLAC__StreamMetadata_VorbisComment_Entry), c_char_p_p, c_char_p_p]
+
+ def FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(entry, field_name, field_value):
+ return libflac.FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(entry, field_name, field_value)
+
+ libflac.FLAC__metadata_object_vorbiscomment_entry_matches.restype = FLAC__bool
+ libflac.FLAC__metadata_object_vorbiscomment_entry_matches.argtypes = [POINTER(FLAC__StreamMetadata_VorbisComment_Entry), c_char_p, c_uint]
+
+ def FLAC__metadata_object_vorbiscomment_entry_matches(entry, field_name, field_value):
+ return libflac.FLAC__metadata_object_vorbiscomment_entry_matches(entry, field_name, field_value)
+
+ libflac.FLAC__metadata_object_vorbiscomment_find_entry_from.restype = c_int
+ libflac.FLAC__metadata_object_vorbiscomment_find_entry_from.argtypes = [POINTER(FLAC__StreamMetadata), c_uint, c_char_p]
+
+ def FLAC__metadata_object_vorbiscomment_find_entry_from(object, offset, field_name):
+ return libflac.FLAC__metadata_object_vorbiscomment_find_entry_from(object, offset, field_name)
+
+ libflac.FLAC__metadata_object_vorbiscomment_remove_entry_matching.restype = c_int
+ libflac.FLAC__metadata_object_vorbiscomment_remove_entry_matching.argtypes = [POINTER(FLAC__StreamMetadata), c_char_p]
+
+ def FLAC__metadata_object_vorbiscomment_remove_entry_matching(object, field_name):
+ return libflac.FLAC__metadata_object_vorbiscomment_remove_entry_matching(object, field_name)
+
+ libflac.FLAC__metadata_object_vorbiscomment_remove_entries_matching.restype = c_int
+ libflac.FLAC__metadata_object_vorbiscomment_remove_entries_matching.argtypes = [POINTER(FLAC__StreamMetadata), c_char_p]
+
+ def FLAC__metadata_object_vorbiscomment_remove_entries_matching(object, field_name):
+ return libflac.FLAC__metadata_object_vorbiscomment_remove_entries_matching(object, field_name)
+
+ libflac.FLAC__metadata_object_cuesheet_track_new.restype = POINTER(FLAC__StreamMetadata_CueSheet_Track)
+ libflac.FLAC__metadata_object_cuesheet_track_new.argtypes = []
+
+ def FLAC__metadata_object_cuesheet_track_new():
+ return libflac.FLAC__metadata_object_cuesheet_track_new()
+
+ libflac.FLAC__metadata_object_cuesheet_track_delete.restype = None
+ libflac.FLAC__metadata_object_cuesheet_track_delete.argtypes = [POINTER(FLAC__StreamMetadata_CueSheet_Track)]
+
+ def FLAC__metadata_object_cuesheet_track_delete(object):
+ return libflac.FLAC__metadata_object_cuesheet_track_delete(object)
+
+ libflac.FLAC__metadata_object_cuesheet_track_resize_indices.restype = FLAC__bool
+ libflac.FLAC__metadata_object_cuesheet_track_resize_indices.argtypes = [POINTER(FLAC__StreamMetadata_CueSheet_Track), c_uint, c_uint]
+
+ def FLAC__metadata_object_cuesheet_track_resize_indices(object, track_num, new_num_indices):
+ return libflac.FLAC__metadata_object_cuesheet_track_resize_indices(object, track_num, new_num_indices)
+
+ libflac.FLAC__metadata_object_cuesheet_track_insert_index.restype = FLAC__bool
+ libflac.FLAC__metadata_object_cuesheet_track_insert_index.argtypes = [POINTER(FLAC__StreamMetadata_CueSheet_Track), c_uint, c_uint, FLAC__StreamMetadata_CueSheet_Index]
+
+ def FLAC__metadata_object_cuesheet_track_insert_index(object, track_num, index_num, index):
+ return libflac.FLAC__metadata_object_cuesheet_track_insert_index(object, track_num, index_num, index)
+
+ libflac.FLAC__metadata_object_cuesheet_track_insert_blank_index.restype = FLAC__bool
+ libflac.FLAC__metadata_object_cuesheet_track_insert_blank_index.argtypes = [POINTER(FLAC__StreamMetadata_CueSheet_Track), c_uint, c_uint]
+
+ def FLAC__metadata_object_cuesheet_track_insert_blank_index(object, track_num, index_num):
+ return libflac.FLAC__metadata_object_cuesheet_track_insert_blank_index(object, track_num, index_num)
+
+ libflac.FLAC__metadata_object_cuesheet_track_delete_index.restype = FLAC__bool
+ libflac.FLAC__metadata_object_cuesheet_track_delete_index.argtypes = [POINTER(FLAC__StreamMetadata_CueSheet_Track), c_uint, c_uint]
+
+ def FLAC__metadata_object_cuesheet_track_delete_index(object, track_num, index_num):
+ return libflac.FLAC__metadata_object_cuesheet_track_delete_index(object, track_num, index_num)
+
+ libflac.FLAC__metadata_object_cuesheet_resize_tracks.restype = FLAC__bool
+ libflac.FLAC__metadata_object_cuesheet_resize_tracks.argtypes = [POINTER(FLAC__StreamMetadata_CueSheet_Track), c_uint]
+
+ def FLAC__metadata_object_cuesheet_resize_tracks(object, new_num_tracks):
+ return libflac.FLAC__metadata_object_cuesheet_resize_tracks(object, new_num_tracks)
+
+ libflac.FLAC__metadata_object_cuesheet_set_track.restype = FLAC__bool
+ libflac.FLAC__metadata_object_cuesheet_set_track.argtypes = [POINTER(FLAC__StreamMetadata_CueSheet_Track), c_uint, POINTER(FLAC__StreamMetadata_CueSheet_Track), FLAC__bool]
+
+ def FLAC__metadata_object_cuesheet_set_track(object, new_num_tracks, track, copy):
+ return libflac.FLAC__metadata_object_cuesheet_set_track(object, new_num_tracks, track, copy)
+
+ libflac.FLAC__metadata_object_cuesheet_insert_track.restype = FLAC__bool
+ libflac.FLAC__metadata_object_cuesheet_insert_track.argtypes = [POINTER(FLAC__StreamMetadata_CueSheet_Track), c_uint, POINTER(FLAC__StreamMetadata_CueSheet_Track), FLAC__bool]
+
+ def FLAC__metadata_object_cuesheet_insert_track(object, track_num, track, copy):
+ return libflac.FLAC__metadata_object_cuesheet_insert_track(object, track_num, track, copy)
+
+ libflac.FLAC__metadata_object_cuesheet_insert_blank_track.restype = FLAC__bool
+ libflac.FLAC__metadata_object_cuesheet_insert_blank_track.argtypes = [POINTER(FLAC__StreamMetadata_CueSheet_Track), c_uint]
+
+ def FLAC__metadata_object_cuesheet_insert_blank_track(object, track_num):
+ return libflac.FLAC__metadata_object_cuesheet_insert_blank_track(object, track_num)
+
+ libflac.FLAC__metadata_object_cuesheet_delete_track.restype = FLAC__bool
+ libflac.FLAC__metadata_object_cuesheet_delete_track.argtypes = [POINTER(FLAC__StreamMetadata_CueSheet_Track), c_uint]
+
+ def FLAC__metadata_object_cuesheet_delete_track(object, track_num):
+ return libflac.FLAC__metadata_object_cuesheet_delete_track(object, track_num)
+
+ libflac.FLAC__metadata_object_cuesheet_is_legal.restype = FLAC__bool
+ libflac.FLAC__metadata_object_cuesheet_is_legal.argtypes = [POINTER(FLAC__StreamMetadata_CueSheet_Track), FLAC__bool, c_char_p_p]
+
+ def FLAC__metadata_object_cuesheet_is_legal(object, check_cd_da_subset, violation):
+ return libflac.FLAC__metadata_object_cuesheet_is_legal(object, check_cd_da_subset, violation)
+
+ libflac.FLAC__metadata_object_cuesheet_calculate_cddb_id.restype = FLAC__uint32
+ libflac.FLAC__metadata_object_cuesheet_calculate_cddb_id.argtypes = [POINTER(FLAC__StreamMetadata_CueSheet_Track)]
+
+ def FLAC__metadata_object_cuesheet_calculate_cddb_id(object):
+ return libflac.FLAC__metadata_object_cuesheet_calculate_cddb_id(object)
+
+ libflac.FLAC__metadata_object_picture_set_mime_type.restype = FLAC__bool
+ libflac.FLAC__metadata_object_picture_set_mime_type.argtypes = [POINTER(FLAC__StreamMetadata_CueSheet_Track), c_char_p, FLAC__bool]
+
+ def FLAC__metadata_object_picture_set_mime_type(object, mime_type, copy):
+ return libflac.FLAC__metadata_object_picture_set_mime_type(object, mime_type, copy)
+
+ libflac.FLAC__metadata_object_picture_set_description.restype = FLAC__bool
+ libflac.FLAC__metadata_object_picture_set_description.argtypes = [POINTER(FLAC__StreamMetadata_CueSheet_Track), FLAC__byte_p, FLAC__bool]
+
+ def FLAC__metadata_object_picture_set_description(object, description, copy):
+ return libflac.FLAC__metadata_object_picture_set_description(object, mime_type, copy)
+
+ libflac.FLAC__metadata_object_picture_set_data.restype = FLAC__bool
+ libflac.FLAC__metadata_object_picture_set_data.argtypes = [POINTER(FLAC__StreamMetadata_CueSheet_Track), FLAC__byte_p,FLAC__uint32, FLAC__bool]
+
+ def FLAC__metadata_object_picture_set_data(object, data, length, copy):
+ return libflac.FLAC__metadata_object_picture_set_data(object, mime_type, copy)
+
+ libflac.FLAC__metadata_object_picture_is_legal.restype = FLAC__bool
+ libflac.FLAC__metadata_object_picture_is_legal.argtypes = [POINTER(FLAC__StreamMetadata_CueSheet_Track), c_char_p]
+
+ def FLAC__metadata_object_picture_is_legal(object, violation):
+ return libflac.FLAC__metadata_object_picture_is_legal(object, violation)
+
+ # /metadata
+
+ # stream_decoder
+
+ FLAC__StreamDecoderState = c_int
+ FLAC__StreamDecoderStateEnum = ["FLAC__STREAM_DECODER_SEARCH_FOR_METADATA",
+ "FLAC__STREAM_DECODER_READ_METADATA",
+ "FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC",
+ "FLAC__STREAM_DECODER_READ_FRAME",
+ "FLAC__STREAM_DECODER_END_OF_STREAM",
+ "FLAC__STREAM_DECODER_OGG_ERROR",
+ "FLAC__STREAM_DECODER_SEEK_ERROR",
+ "FLAC__STREAM_DECODER_ABORTED",
+ "FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR",
+ "FLAC__STREAM_DECODER_UNINITIALIZED"]
+
+ libflac.FLAC__StreamDecoderStateString.restype = c_char_p
+ libflac.FLAC__StreamDecoderStateString.argtypes = []
+
+ def FLAC__StreamDecoderStateString():
+ return libflac.FLAC__StreamDecoderStateString()
+
+
+ FLAC__StreamDecoderInitStatus = c_int
+ FLAC__StreamDecoderInitStatusEnum = ["FLAC__STREAM_DECODER_INIT_STATUS_OK",
+ "FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER",
+ "FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS",
+ "FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR",
+ "FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE",
+ "FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED"]
+
+ libflac.FLAC__StreamDecoderInitStatusString.restype = c_char_p
+ libflac.FLAC__StreamDecoderInitStatusString.argtypes = []
+
+ def FLAC__StreamDecoderInitStatusString():
+ return libflac.FLAC__StreamDecoderInitStatusString()
+
+
+ FLAC__StreamDecoderReadStatus = c_int
+ FLAC__StreamDecoderReadStatusEnum = ["FLAC__STREAM_DECODER_READ_STATUS_CONTINUE",
+ "FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM",
+ "FLAC__STREAM_DECODER_READ_STATUS_ABORT"]
+
+ libflac.FLAC__StreamDecoderReadStatusString.restype = c_char_p
+ libflac.FLAC__StreamDecoderReadStatusString.argtypes = []
+
+ def FLAC__StreamDecoderReadStatusString():
+ return libflac.FLAC__StreamDecoderReadStatusString()
+
+
+ FLAC__StreamDecoderSeekStatus = c_int
+ FLAC__StreamDecoderSeekStatusEnum = ["FLAC__STREAM_DECODER_SEEK_STATUS_OK",
+ "FLAC__STREAM_DECODER_SEEK_STATUS_ERROR",
+ "FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED"]
+
+ libflac.FLAC__StreamDecoderSeekStatusString.restype = c_char_p
+ libflac.FLAC__StreamDecoderSeekStatusString.argtypes = []
+
+ def FLAC__StreamDecoderSeekStatusString():
+ return libflac.FLAC__StreamDecoderSeekStatusString()
+
+
+ FLAC__StreamDecoderTellStatus = c_int
+ FLAC__StreamDecoderTellStatusEnum = ["FLAC__STREAM_DECODER_TELL_STATUS_OK",
+ "FLAC__STREAM_DECODER_TELL_STATUS_ERROR",
+ "FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED"]
+
+ libflac.FLAC__StreamDecoderTellStatusString.restype = c_char_p
+ libflac.FLAC__StreamDecoderTellStatusString.argtypes = []
+
+ def FLAC__StreamDecoderTellStatusString():
+ return libflac.FLAC__StreamDecoderTellStatusString()
+
+
+ FLAC__StreamDecoderLengthStatus = c_int
+ FLAC__StreamDecoderLengthStatusEnum = ["FLAC__STREAM_DECODER_LENGTH_STATUS_OK",
+ "FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR",
+ "FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED"]
+
+ libflac.FLAC__StreamDecoderLengthStatusString.restype = c_char_p
+ libflac.FLAC__StreamDecoderLengthStatusString.argtypes = []
+
+ def FLAC__StreamDecoderLengthStatusString():
+ return libflac.FLAC__StreamDecoderLengthStatusString()
+
+
+ FLAC__StreamDecoderWriteStatus = c_int
+ FLAC__StreamDecoderWriteStatusEnum = ["FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE", "FLAC__STREAM_DECODER_WRITE_STATUS_ABORT"]
+
+ libflac.FLAC__StreamDecoderWriteStatusString.restype = c_char_p
+ libflac.FLAC__StreamDecoderWriteStatusString.argtypes = []
+
+ def FLAC__StreamDecoderWriteStatusString():
+ return libflac.FLAC__StreamDecoderWriteStatusString()
+
+ FLAC__StreamDecoderErrorStatus = c_int
+ FLAC__StreamDecoderErrorStatusEnum = ["FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC",
+ "FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER",
+ "FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH",
+ "FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM"]
+
+ libflac.FLAC__StreamDecoderErrorStatusString.restype = c_char_p
+ libflac.FLAC__StreamDecoderErrorStatusString.argtypes = []
+
+ def FLAC__StreamDecoderErrorStatusString():
+ return libflac.FLAC__StreamDecoderErrorStatusString()
+
+
+
+ class FLAC__StreamDecoderProtected(Structure):
+ _fields_ = [("dummy", c_int)]
+
+ class FLAC__StreamDecoderPrivate(Structure):
+ _fields_ = [("dummy", c_int)]
+
+ class FLAC__StreamDecoder(Structure):
+ _fields_ = [("protected_", POINTER(FLAC__StreamDecoderProtected)),
+ ("private_", POINTER(FLAC__StreamDecoderPrivate))]
+
+ FLAC__StreamDecoderReadCallback = CFUNCTYPE(
+ FLAC__StreamDecoderReadStatus,
+ POINTER(FLAC__StreamDecoder),
+ POINTER(FLAC__byte*0),
+ c_size_t_p,
+ c_void_p
+ )
+
+ FLAC__StreamDecoderSeekCallback = CFUNCTYPE(
+ FLAC__StreamDecoderSeekStatus,
+ POINTER(FLAC__StreamDecoder),
+ FLAC__uint64,
+ c_void_p
+ )
+
+ FLAC__StreamDecoderTellCallback = CFUNCTYPE(
+ FLAC__StreamDecoderTellStatus,
+ POINTER(FLAC__StreamDecoder),
+ FLAC__uint64_p,
+ c_void_p
+ )
+
+ FLAC__StreamDecoderLengthCallback = CFUNCTYPE(
+ FLAC__StreamDecoderLengthStatus,
+ POINTER(FLAC__StreamDecoder),
+ FLAC__uint64_p,
+ c_void_p
+ )
+
+ FLAC__StreamDecoderEofCallback = CFUNCTYPE(
+ FLAC__bool,
+ POINTER(FLAC__StreamDecoder),
+ c_void_p
+ )
+
+ FLAC__StreamDecoderWriteCallback = CFUNCTYPE(
+ FLAC__StreamDecoderWriteStatus,
+ POINTER(FLAC__StreamDecoder),
+ POINTER(FLAC__Frame),
+ POINTER(FLAC__int32_p*0),
+ c_void_p
+ )
+
+ FLAC__StreamDecoderMetadataCallback = CFUNCTYPE(
+ None,
+ POINTER(FLAC__StreamDecoder),
+ POINTER(FLAC__StreamMetadata),
+ c_void_p
+ )
+
+ FLAC__StreamDecoderErrorCallback = CFUNCTYPE(
+ None,
+ POINTER(FLAC__StreamDecoder),
+ FLAC__StreamDecoderErrorStatus,
+ c_void_p
+ )
+
+
+ libflac.FLAC__stream_decoder_new.restype = POINTER(FLAC__StreamDecoder)
+ libflac.FLAC__stream_decoder_new.argtypes = []
+
+ def FLAC__stream_decoder_new():
+ return libflac.FLAC__stream_decoder_new()
+
+ libflac.FLAC__stream_decoder_delete.restype = None
+ libflac.FLAC__stream_decoder_delete.argtypes = [POINTER(FLAC__StreamDecoder)]
+
+ def FLAC__stream_decoder_delete(decoder):
+ return libflac.FLAC__stream_decoder_delete(decoder)
+
+
+ libflac.FLAC__stream_decoder_set_ogg_serial_number.restype = FLAC__bool
+ libflac.FLAC__stream_decoder_set_ogg_serial_number.argtypes = [POINTER(FLAC__StreamDecoder), c_long]
+
+ def FLAC__stream_decoder_set_ogg_serial_number(decoder, serial_number):
+ return libflac.FLAC__stream_decoder_set_ogg_serial_number(decoder, serial_number)
+
+ libflac.FLAC__stream_decoder_set_md5_checking.restype = FLAC__bool
+ libflac.FLAC__stream_decoder_set_md5_checking.argtypes = [POINTER(FLAC__StreamDecoder), FLAC__bool]
+
+ def FLAC__stream_decoder_set_md5_checking(decoder, value):
+ return libflac.FLAC__stream_decoder_set_md5_checking(decoder, value)
+
+ libflac.FLAC__stream_decoder_set_metadata_respond.restype = FLAC__bool
+ libflac.FLAC__stream_decoder_set_metadata_respond.argtypes = [POINTER(FLAC__StreamDecoder), FLAC__MetadataType]
+
+ def FLAC__stream_decoder_set_metadata_respond(decoder, type):
+ return libflac.FLAC__stream_decoder_set_metadata_respond(decoder, type)
+
+ libflac.FLAC__stream_decoder_set_metadata_respond_application.restype = FLAC__bool
+ libflac.FLAC__stream_decoder_set_metadata_respond_application.argtypes = [POINTER(FLAC__StreamDecoder), FLAC__byte*4]
+
+ def FLAC__stream_decoder_set_metadata_respond_application(decoder, id):
+ return libflac.FLAC__stream_decoder_set_metadata_respond_application(decoder, id)
+
+ libflac.FLAC__stream_decoder_set_metadata_respond_all.restype = FLAC__bool
+ libflac.FLAC__stream_decoder_set_metadata_respond_all.argtypes = [POINTER(FLAC__StreamDecoder)]
+
+ def FLAC__stream_decoder_set_metadata_respond_all(decoder):
+ return libflac.FLAC__stream_decoder_set_metadata_respond_all(decoder)
+
+ libflac.FLAC__stream_decoder_set_metadata_ignore.restype = FLAC__bool
+ libflac.FLAC__stream_decoder_set_metadata_ignore.argtypes = [POINTER(FLAC__StreamDecoder), FLAC__MetadataType]
+
+ def FLAC__stream_decoder_set_metadata_ignore(decoder, type):
+ return libflac.FLAC__stream_decoder_set_metadata_ignore(decoder, type)
+
+ libflac.FLAC__stream_decoder_set_metadata_ignore_application.restype = FLAC__bool
+ libflac.FLAC__stream_decoder_set_metadata_ignore_application.argtypes = [POINTER(FLAC__StreamDecoder), FLAC__byte*4]
+
+ def FLAC__stream_decoder_set_metadata_ignore_application(decoder, id):
+ return libflac.FLAC__stream_decoder_set_metadata_ignore_application(decoder, id)
+
+ libflac.FLAC__stream_decoder_set_metadata_ignore_all.restype = FLAC__bool
+ libflac.FLAC__stream_decoder_set_metadata_ignore_all.argtypes = [POINTER(FLAC__StreamDecoder)]
+
+ def FLAC__stream_decoder_set_metadata_ignore_all(decoder):
+ return libflac.FLAC__stream_decoder_set_metadata_ignore_all(decoder)
+
+ libflac.FLAC__stream_decoder_get_state.restype = FLAC__StreamDecoderState
+ libflac.FLAC__stream_decoder_get_state.argtypes = [POINTER(FLAC__StreamDecoder)]
+
+ def FLAC__stream_decoder_get_state(decoder):
+ return libflac.FLAC__stream_decoder_get_state(decoder)
+
+ libflac.FLAC__stream_decoder_get_resolved_state_string.restype = c_char_p
+ libflac.FLAC__stream_decoder_get_resolved_state_string.argtypes = [POINTER(FLAC__StreamDecoder)]
+
+ def FLAC__stream_decoder_get_resolved_state_string(decoder):
+ return libflac.FLAC__stream_decoder_get_resolved_state_string(decoder)
+
+ libflac.FLAC__stream_decoder_get_md5_checking.restype = FLAC__bool
+ libflac.FLAC__stream_decoder_get_md5_checking.argtypes = [POINTER(FLAC__StreamDecoder)]
+
+ def FLAC__stream_decoder_get_md5_checking(decoder):
+ return libflac.FLAC__stream_decoder_get_md5_checking(decoder)
+
+ libflac.FLAC__stream_decoder_get_total_samples.restype = FLAC__uint64
+ libflac.FLAC__stream_decoder_get_total_samples.argtypes = [POINTER(FLAC__StreamDecoder)]
+
+ def FLAC__stream_decoder_get_total_samples(decoder):
+ return libflac.FLAC__stream_decoder_get_total_samples(decoder)
+
+ libflac.FLAC__stream_decoder_get_channels.restype = c_uint
+ libflac.FLAC__stream_decoder_get_channels.argtypes = [POINTER(FLAC__StreamDecoder)]
+
+ def FLAC__stream_decoder_get_channels(decoder):
+ return libflac.FLAC__stream_decoder_get_channels(decoder)
+
+ libflac.FLAC__stream_decoder_get_channel_assignment.restype = FLAC__ChannelAssignment
+ libflac.FLAC__stream_decoder_get_channel_assignment.argtypes = [POINTER(FLAC__StreamDecoder)]
+
+ def FLAC__stream_decoder_get_channel_assignment(decoder):
+ return libflac.FLAC__stream_decoder_get_channel_assignment(decoder)
+
+ libflac.FLAC__stream_decoder_get_bits_per_sample.restype = c_uint
+ libflac.FLAC__stream_decoder_get_bits_per_sample.argtypes = [POINTER(FLAC__StreamDecoder)]
+
+ def FLAC__stream_decoder_get_bits_per_sample(decoder):
+ return libflac.FLAC__stream_decoder_get_bits_per_sample(decoder)
+
+ libflac.FLAC__stream_decoder_get_sample_rate.restype = c_uint
+ libflac.FLAC__stream_decoder_get_sample_rate.argtypes = [POINTER(FLAC__StreamDecoder)]
+
+ def FLAC__stream_decoder_get_sample_rate(decoder):
+ return libflac.FLAC__stream_decoder_get_sample_rate(decoder)
+
+ libflac.FLAC__stream_decoder_get_blocksize.restype = c_uint
+ libflac.FLAC__stream_decoder_get_blocksize.argtypes = [POINTER(FLAC__StreamDecoder)]
+
+ def FLAC__stream_decoder_get_blocksize(decoder):
+ return libflac.FLAC__stream_decoder_get_blocksize(decoder)
+
+ libflac.FLAC__stream_decoder_get_decode_position.restype = FLAC__bool
+ libflac.FLAC__stream_decoder_get_decode_position.argtypes = [POINTER(FLAC__StreamDecoder), FLAC__uint64_p]
+
+ def FLAC__stream_decoder_get_decode_position(decoder, position):
+ return libflac.FLAC__stream_decoder_get_decode_position(decoder, position)
+
+ libflac.FLAC__stream_decoder_init_stream.restype = FLAC__StreamDecoderInitStatus
+ libflac.FLAC__stream_decoder_init_stream.argtypes = [POINTER(FLAC__StreamDecoder),
+ FLAC__StreamDecoderReadCallback,
+ FLAC__StreamDecoderSeekCallback,
+ FLAC__StreamDecoderTellCallback,
+ FLAC__StreamDecoderLengthCallback,
+ FLAC__StreamDecoderEofCallback,
+ FLAC__StreamDecoderWriteCallback,
+ FLAC__StreamDecoderMetadataCallback,
+ FLAC__StreamDecoderErrorCallback,
+ c_void_p]
+
+ def FLAC__stream_decoder_init_stream(decoder, read_callback, seek_callback, tell_callback, length_callback, eof_callback, write_callback, metadata_callback, error_callback, client_data):
+ return libflac.FLAC__stream_decoder_init_stream(decoder, read_callback, seek_callback, tell_callback, length_callback, eof_callback, write_callback, metadata_callback, error_callback, client_data)
+
+
+ libflac.FLAC__stream_decoder_init_ogg_stream.restype = FLAC__StreamDecoderInitStatus
+ libflac.FLAC__stream_decoder_init_ogg_stream.argtypes = [POINTER(FLAC__StreamDecoder),
+ FLAC__StreamDecoderReadCallback,
+ FLAC__StreamDecoderSeekCallback,
+ FLAC__StreamDecoderTellCallback,
+ FLAC__StreamDecoderLengthCallback,
+ FLAC__StreamDecoderEofCallback,
+ FLAC__StreamDecoderWriteCallback,
+ FLAC__StreamDecoderMetadataCallback,
+ FLAC__StreamDecoderErrorCallback,
+ c_void_p]
+
+ def FLAC__stream_decoder_init_ogg_stream(decoder, read_callback, seek_callback, tell_callback, length_callback, eof_callback, write_callback, metadata_callback, error_callback, client_data):
+ return libflac.FLAC__stream_decoder_init_ogg_stream(decoder, read_callback, seek_callback, tell_callback, length_callback, eof_callback, write_callback, metadata_callback, error_callback, client_data)
+
+ libflac.FLAC__stream_decoder_init_file.restype = FLAC__StreamDecoderInitStatus
+ libflac.FLAC__stream_decoder_init_file.argtypes = [POINTER(FLAC__StreamDecoder),
+ c_char_p,
+ FLAC__StreamDecoderWriteCallback,
+ FLAC__StreamDecoderMetadataCallback,
+ FLAC__StreamDecoderErrorCallback,
+ c_void_p]
+
+ def FLAC__stream_decoder_init_file(decoder, filename, write_callback, metadata_callback, error_callback, client_data):
+ return libflac.FLAC__stream_decoder_init_file(decoder, filename, write_callback, metadata_callback, error_callback, client_data)
+
+ libflac.FLAC__stream_decoder_init_ogg_file.restype = FLAC__StreamDecoderInitStatus
+ libflac.FLAC__stream_decoder_init_ogg_file.argtypes = [POINTER(FLAC__StreamDecoder),
+ c_char_p,
+ FLAC__StreamDecoderWriteCallback,
+ FLAC__StreamDecoderMetadataCallback,
+ FLAC__StreamDecoderErrorCallback,
+ c_void_p]
+
+ def FLAC__stream_decoder_init_ogg_file(decoder, filename, write_callback, metadata_callback, error_callback, client_data):
+ return libflac.FLAC__stream_decoder_init_ogg_file(decoder, filename, write_callback, metadata_callback, error_callback, client_data)
+
+ libflac.FLAC__stream_decoder_finish.restype = FLAC__bool
+ libflac.FLAC__stream_decoder_finish.argtypes = [POINTER(FLAC__StreamDecoder)]
+
+ def FLAC__stream_decoder_finish(decoder):
+ return libflac.FLAC__stream_decoder_finish(decoder)
+
+ libflac.FLAC__stream_decoder_flush.restype = FLAC__bool
+ libflac.FLAC__stream_decoder_flush.argtypes = [POINTER(FLAC__StreamDecoder)]
+
+ def FLAC__stream_decoder_flush(decoder):
+ return libflac.FLAC__stream_decoder_flush(decoder)
+
+ libflac.FLAC__stream_decoder_reset.restype = FLAC__bool
+ libflac.FLAC__stream_decoder_reset.argtypes = [POINTER(FLAC__StreamDecoder)]
+
+ def FLAC__stream_decoder_reset(decoder):
+ return libflac.FLAC__stream_decoder_reset(decoder)
+
+ libflac.FLAC__stream_decoder_process_single.restype = FLAC__bool
+ libflac.FLAC__stream_decoder_process_single.argtypes = [POINTER(FLAC__StreamDecoder)]
+
+ def FLAC__stream_decoder_process_single(decoder):
+ return libflac.FLAC__stream_decoder_process_single(decoder)
+
+ libflac.FLAC__stream_decoder_process_until_end_of_metadata.restype = FLAC__bool
+ libflac.FLAC__stream_decoder_process_until_end_of_metadata.argtypes = [POINTER(FLAC__StreamDecoder)]
+
+ def FLAC__stream_decoder_process_until_end_of_metadata(decoder):
+ return libflac.FLAC__stream_decoder_process_until_end_of_metadata(decoder)
+
+ libflac.FLAC__stream_decoder_process_until_end_of_stream.restype = FLAC__bool
+ libflac.FLAC__stream_decoder_process_until_end_of_stream.argtypes = [POINTER(FLAC__StreamDecoder)]
+
+ def FLAC__stream_decoder_process_until_end_of_stream(decoder):
+ return libflac.FLAC__stream_decoder_process_until_end_of_stream(decoder)
+
+ libflac.FLAC__stream_decoder_skip_single_frame.restype = FLAC__bool
+ libflac.FLAC__stream_decoder_skip_single_frame.argtypes = [POINTER(FLAC__StreamDecoder)]
+
+ def FLAC__stream_decoder_skip_single_frame(decoder):
+ return libflac.FLAC__stream_decoder_skip_single_frame(decoder)
+
+ libflac.FLAC__stream_decoder_seek_absolute.restype = FLAC__bool
+ libflac.FLAC__stream_decoder_seek_absolute.argtypes = [POINTER(FLAC__StreamDecoder), FLAC__uint64]
+
+ def FLAC__stream_decoder_seek_absolute(decoder, sample):
+ return libflac.FLAC__stream_decoder_seek_absolute(decoder, sample)
+
+ # /stream_decoder
+
+ # stream_encoder
+
+ FLAC__StreamEncoderState = c_int
+
+ libflac.FLAC__StreamEncoderStateString.restype = c_char_p
+ libflac.FLAC__StreamEncoderStateString.argtypes = []
+
+ def FLAC__StreamEncoderStateString():
+ return libflac.FLAC__StreamEncoderStateString()
+
+
+ FLAC__StreamEncoderInitStatus = c_int
+
+ libflac.FLAC__StreamEncoderInitStatusString.restype = c_char_p
+ libflac.FLAC__StreamEncoderInitStatusString.argtypes = []
+
+ def FLAC__StreamEncoderInitStatusString():
+ return libflac.FLAC__StreamEncoderInitStatusString()
+
+
+ FLAC__StreamEncoderReadStatus = c_int
+
+ libflac.FLAC__StreamEncoderReadStatusString.restype = c_char_p
+ libflac.FLAC__StreamEncoderReadStatusString.argtypes = []
+
+ def FLAC__StreamEncoderReadStatusString():
+ return libflac.FLAC__StreamEncoderReadStatusString()
+
+
+ FLAC__StreamEncoderWriteStatus = c_int
+
+ libflac.FLAC__StreamEncoderWriteStatusString.restype = c_char_p
+ libflac.FLAC__StreamEncoderWriteStatusString.argtypes = []
+
+ def FLAC__StreamEncoderWriteStatusString():
+ return libflac.FLAC__StreamEncoderWriteStatusString()
+
+
+ FLAC__StreamEncoderSeekStatus = c_int
+
+ libflac.FLAC__StreamEncoderSeekStatusString.restype = c_char_p
+ libflac.FLAC__StreamEncoderSeekStatusString.argtypes = []
+
+ def FLAC__StreamEncoderSeekStatusString():
+ return libflac.FLAC__StreamEncoderSeekStatusString()
+
+
+ FLAC__StreamEncoderTellStatus = c_int
+
+ libflac.FLAC__StreamEncoderTellStatusString.restype = c_char_p
+ libflac.FLAC__StreamEncoderTellStatusString.argtypes = []
+
+ def FLAC__StreamEncoderTellStatusString():
+ return libflac.FLAC__StreamEncoderTellStatusString()
+
+
+ class FLAC__StreamEncoderProtected(Structure):
+ _fields_ = [("dummy", c_int)]
+
+ class FLAC__StreamEncoderPrivate(Structure):
+ _fields_ = [("dummy", c_int)]
+
+ class FLAC__StreamEncoder(Structure):
+ _fields_ = [("protected_", POINTER(FLAC__StreamEncoderProtected)),
+ ("private_", POINTER(FLAC__StreamEncoderPrivate))]
+
+ FLAC__StreamEncoderReadCallback = CFUNCTYPE(FLAC__StreamEncoderReadStatus, POINTER(FLAC__StreamEncoder), POINTER(FLAC__byte*0), c_size_t_p, c_void_p)
+
+ FLAC__StreamEncoderWriteCallback = CFUNCTYPE(FLAC__StreamEncoderWriteStatus, POINTER(FLAC__StreamEncoder), POINTER(FLAC__byte*0), c_size_t, c_uint, c_uint, c_void_p)
+
+ FLAC__StreamEncoderSeekCallback = CFUNCTYPE(FLAC__StreamEncoderSeekStatus, POINTER(FLAC__StreamEncoder), FLAC__uint64, c_void_p)
+
+ FLAC__StreamEncoderTellCallback = CFUNCTYPE(FLAC__StreamEncoderTellStatus, POINTER(FLAC__StreamEncoder), FLAC__uint64_p, c_void_p)
+
+ FLAC__StreamEncoderMetadataCallback = CFUNCTYPE(None, POINTER(FLAC__StreamEncoder), POINTER(FLAC__StreamMetadata), c_void_p)
+
+ FLAC__StreamEncoderProgressCallback = CFUNCTYPE(None, POINTER(FLAC__StreamEncoder), FLAC__uint64,FLAC__uint64, c_uint, c_uint, c_void_p)
+
+
+ libflac.FLAC__stream_encoder_new.restype = POINTER(FLAC__StreamEncoder)
+ libflac.FLAC__stream_encoder_new.argtypes = []
+
+ def FLAC__stream_encoder_new():
+ return libflac.FLAC__stream_encoder_new()
+
+ libflac.FLAC__stream_encoder_delete.restype = None
+ libflac.FLAC__stream_encoder_delete.argtypes = [POINTER(FLAC__StreamEncoder)]
+
+ def FLAC__stream_encoder_delete(encoder):
+ return libflac.FLAC__stream_encoder_delete(encoder)
+
+
+ libflac.FLAC__stream_encoder_set_ogg_serial_number.restype = FLAC__bool
+ libflac.FLAC__stream_encoder_set_ogg_serial_number.argtypes = [POINTER(FLAC__StreamEncoder), c_long]
+
+ def FLAC__stream_encoder_set_ogg_serial_number(encoder, serial_number):
+ return libflac.FLAC__stream_encoder_set_ogg_serial_number(encoder, serial_number)
+
+ libflac.FLAC__stream_encoder_set_verify.restype = FLAC__bool
+ libflac.FLAC__stream_encoder_set_verify.argtypes = [POINTER(FLAC__StreamEncoder), FLAC__bool]
+
+ def FLAC__stream_encoder_set_verify(encoder, value):
+ return libflac.FLAC__stream_encoder_set_verify(encoder, value)
+
+ libflac.FLAC__stream_encoder_set_streamable_subset.restype = FLAC__bool
+ libflac.FLAC__stream_encoder_set_streamable_subset.argtypes = [POINTER(FLAC__StreamEncoder), FLAC__bool]
+
+ def FLAC__stream_encoder_set_streamable_subset(encoder, value):
+ return libflac.FLAC__stream_encoder_set_streamable_subset(encoder, value)
+
+ libflac.FLAC__stream_encoder_set_channels.restype = FLAC__bool
+ libflac.FLAC__stream_encoder_set_channels.argtypes = [POINTER(FLAC__StreamEncoder), c_uint]
+
+ def FLAC__stream_encoder_set_channels(encoder, value):
+ return libflac.FLAC__stream_encoder_set_channels(encoder, value)
+
+ libflac.FLAC__stream_encoder_set_bits_per_sample.restype = FLAC__bool
+ libflac.FLAC__stream_encoder_set_bits_per_sample.argtypes = [POINTER(FLAC__StreamEncoder), c_uint]
+
+ def FLAC__stream_encoder_set_bits_per_sample(encoder, value):
+ return libflac.FLAC__stream_encoder_set_bits_per_sample(encoder, value)
+
+ libflac.FLAC__stream_encoder_set_sample_rate.restype = FLAC__bool
+ libflac.FLAC__stream_encoder_set_sample_rate.argtypes = [POINTER(FLAC__StreamEncoder), c_uint]
+
+ def FLAC__stream_encoder_set_sample_rate(encoder, value):
+ return libflac.FLAC__stream_encoder_set_sample_rate(encoder, value)
+
+ libflac.FLAC__stream_encoder_set_compression_level.restype = FLAC__bool
+ libflac.FLAC__stream_encoder_set_compression_level.argtypes = [POINTER(FLAC__StreamEncoder), c_uint]
+
+ def FLAC__stream_encoder_set_compression_level(encoder, value):
+ return libflac.FLAC__stream_encoder_set_compression_level(encoder, value)
+
+ libflac.FLAC__stream_encoder_set_blocksize.restype = FLAC__bool
+ libflac.FLAC__stream_encoder_set_blocksize.argtypes = [POINTER(FLAC__StreamEncoder), c_uint]
+
+ def FLAC__stream_encoder_set_blocksize(encoder, value):
+ return libflac.FLAC__stream_encoder_set_blocksize(encoder, value)
+
+ libflac.FLAC__stream_encoder_set_do_mid_side_stereo.restype = FLAC__bool
+ libflac.FLAC__stream_encoder_set_do_mid_side_stereo.argtypes = [POINTER(FLAC__StreamEncoder), FLAC__bool]
+
+ def FLAC__stream_encoder_set_do_mid_side_stereo(encoder, value):
+ return libflac.FLAC__stream_encoder_set_do_mid_side_stereo(encoder, value)
+
+ libflac.FLAC__stream_encoder_set_loose_mid_side_stereo.restype = FLAC__bool
+ libflac.FLAC__stream_encoder_set_loose_mid_side_stereo.argtypes = [POINTER(FLAC__StreamEncoder), FLAC__bool]
+
+ def FLAC__stream_encoder_set_loose_mid_side_stereo(encoder, value):
+ return libflac.FLAC__stream_encoder_set_loose_mid_side_stereo(encoder, value)
+
+ libflac.FLAC__stream_encoder_set_apodization.restype = FLAC__bool
+ libflac.FLAC__stream_encoder_set_apodization.argtypes = [POINTER(FLAC__StreamEncoder), c_char_p]
+
+ def FLAC__stream_encoder_set_apodization(encoder, specification):
+ return libflac.FLAC__stream_encoder_set_apodization(encoder, specification)
+
+ libflac.FLAC__stream_encoder_set_max_lpc_order.restype = FLAC__bool
+ libflac.FLAC__stream_encoder_set_max_lpc_order.argtypes = [POINTER(FLAC__StreamEncoder), c_uint]
+
+ def FLAC__stream_encoder_set_max_lpc_order(encoder, value):
+ return libflac.FLAC__stream_encoder_set_max_lpc_order(encoder, value)
+
+ libflac.FLAC__stream_encoder_set_qlp_coeff_precision.restype = FLAC__bool
+ libflac.FLAC__stream_encoder_set_qlp_coeff_precision.argtypes = [POINTER(FLAC__StreamEncoder), c_uint]
+
+ def FLAC__stream_encoder_set_qlp_coeff_precision(encoder, value):
+ return libflac.FLAC__stream_encoder_set_qlp_coeff_precision(encoder, value)
+
+ libflac.FLAC__stream_encoder_set_do_qlp_coeff_prec_search.restype = FLAC__bool
+ libflac.FLAC__stream_encoder_set_do_qlp_coeff_prec_search.argtypes = [POINTER(FLAC__StreamEncoder), FLAC__bool]
+
+ def FLAC__stream_encoder_set_do_qlp_coeff_prec_search(encoder, value):
+ return libflac.FLAC__stream_encoder_set_do_qlp_coeff_prec_search(encoder, value)
+
+ libflac.FLAC__stream_encoder_set_do_escape_coding.restype = FLAC__bool
+ libflac.FLAC__stream_encoder_set_do_escape_coding.argtypes = [POINTER(FLAC__StreamEncoder), FLAC__bool]
+
+ def FLAC__stream_encoder_set_do_escape_coding(encoder, value):
+ return libflac.FLAC__stream_encoder_set_do_escape_coding(encoder, value)
+
+ libflac.FLAC__stream_encoder_set_do_exhaustive_model_search.restype = FLAC__bool
+ libflac.FLAC__stream_encoder_set_do_exhaustive_model_search.argtypes = [POINTER(FLAC__StreamEncoder), FLAC__bool]
+
+ def FLAC__stream_encoder_set_do_exhaustive_model_search(encoder, value):
+ return libflac.FLAC__stream_encoder_set_do_exhaustive_model_search(encoder, value)
+
+ libflac.FLAC__stream_encoder_set_min_residual_partition_order.restype = FLAC__bool
+ libflac.FLAC__stream_encoder_set_min_residual_partition_order.argtypes = [POINTER(FLAC__StreamEncoder), FLAC__bool]
+
+ def FLAC__stream_encoder_set_min_residual_partition_order(encoder, value):
+ return libflac.FLAC__stream_encoder_set_min_residual_partition_order(encoder, value)
+
+ libflac.FLAC__stream_encoder_set_max_residual_partition_order.restype = FLAC__bool
+ libflac.FLAC__stream_encoder_set_max_residual_partition_order.argtypes = [POINTER(FLAC__StreamEncoder), FLAC__bool]
+
+ def FLAC__stream_encoder_set_max_residual_partition_order(encoder, value):
+ return libflac.FLAC__stream_encoder_set_max_residual_partition_order(encoder, value)
+
+ libflac.FLAC__stream_encoder_set_rice_parameter_search_dist.restype = FLAC__bool
+ libflac.FLAC__stream_encoder_set_rice_parameter_search_dist.argtypes = [POINTER(FLAC__StreamEncoder), FLAC__bool]
+
+ def FLAC__stream_encoder_set_rice_parameter_search_dist(encoder, value):
+ return libflac.FLAC__stream_encoder_set_rice_parameter_search_dist(encoder, value)
+
+ libflac.FLAC__stream_encoder_set_total_samples_estimate.restype = FLAC__bool
+ libflac.FLAC__stream_encoder_set_total_samples_estimate.argtypes = [POINTER(FLAC__StreamEncoder), FLAC__uint64]
+
+ def FLAC__stream_encoder_set_total_samples_estimate(encoder, value):
+ return libflac.FLAC__stream_encoder_set_total_samples_estimate(encoder, value)
+
+ libflac.FLAC__stream_encoder_set_metadata.restype = FLAC__bool
+ libflac.FLAC__stream_encoder_set_metadata.argtypes = [POINTER(FLAC__StreamEncoder), POINTER(POINTER(FLAC__StreamMetadata)), c_uint]
+
+ def FLAC__stream_encoder_set_metadata(encoder, metadata, num_blocks):
+ return libflac.FLAC__stream_encoder_set_metadata(encoder, metadata, num_blocks)
+
+ libflac.FLAC__stream_encoder_get_state.restype = FLAC__StreamEncoderState
+ libflac.FLAC__stream_encoder_get_state.argtypes = [POINTER(FLAC__StreamEncoder)]
+
+ def FLAC__stream_encoder_get_state(encoder):
+ return libflac.FLAC__stream_encoder_get_state(encoder)
+
+ libflac.FLAC__stream_encoder_get_verify_decoder_state.restype = FLAC__StreamEncoderState
+ libflac.FLAC__stream_encoder_get_verify_decoder_state.argtypes = [POINTER(FLAC__StreamEncoder)]
+
+ def FLAC__stream_encoder_get_verify_decoder_state(encoder):
+ return libflac.FLAC__stream_encoder_get_verify_decoder_state(encoder)
+
+ libflac.FLAC__stream_encoder_get_resolved_state_string.restype = c_char_p
+ libflac.FLAC__stream_encoder_get_resolved_state_string.argtypes = [POINTER(FLAC__StreamEncoder)]
+
+ def FLAC__stream_encoder_get_resolved_state_string(encoder):
+ return libflac.FLAC__stream_encoder_get_resolved_state_string(encoder)
+
+ libflac.FLAC__stream_encoder_get_verify_decoder_error_stats.restype = None
+ libflac.FLAC__stream_encoder_get_verify_decoder_error_stats.argtypes = [POINTER(FLAC__StreamEncoder), FLAC__uint64_p, c_uint_p, c_uint_p, c_uint_p, FLAC__int32_p, FLAC__int32_p]
+
+ def FLAC__stream_encoder_get_verify_decoder_error_stats(encoder, absolute_sample, frame_number, channel, sample, expected, got):
+ return libflac.FLAC__stream_encoder_get_verify_decoder_error_stats(encoder, absolute_sample, frame_number, channel, sample, expected, got)
+
+ libflac.FLAC__stream_encoder_get_verify.restype = FLAC__bool
+ libflac.FLAC__stream_encoder_get_verify.argtypes = [POINTER(FLAC__StreamEncoder)]
+
+ def FLAC__stream_encoder_get_verify(encoder):
+ return libflac.FLAC__stream_encoder_get_verify(encoder)
+
+ libflac.FLAC__stream_encoder_get_streamable_subset.restype = FLAC__bool
+ libflac.FLAC__stream_encoder_get_streamable_subset.argtypes = [POINTER(FLAC__StreamEncoder)]
+
+ def FLAC__stream_encoder_get_streamable_subset(encoder):
+ return libflac.FLAC__stream_encoder_get_streamable_subset(encoder)
+
+ libflac.FLAC__stream_encoder_get_channels.restype = c_uint
+ libflac.FLAC__stream_encoder_get_channels.argtypes = [POINTER(FLAC__StreamEncoder)]
+
+ def FLAC__stream_encoder_get_channels(encoder):
+ return libflac.FLAC__stream_encoder_get_channels(encoder)
+
+ libflac.FLAC__stream_encoder_get_bits_per_sample.restype = c_uint
+ libflac.FLAC__stream_encoder_get_bits_per_sample.argtypes = [POINTER(FLAC__StreamEncoder)]
+
+ def FLAC__stream_encoder_get_bits_per_sample(encoder):
+ return libflac.FLAC__stream_encoder_get_bits_per_sample(encoder)
+
+ libflac.FLAC__stream_encoder_get_sample_rate.restype = c_uint
+ libflac.FLAC__stream_encoder_get_sample_rate.argtypes = [POINTER(FLAC__StreamEncoder)]
+
+ def FLAC__stream_encoder_get_sample_rate(encoder):
+ return libflac.FLAC__stream_encoder_get_sample_rate(encoder)
+
+ libflac.FLAC__stream_encoder_get_blocksize.restype = c_uint
+ libflac.FLAC__stream_encoder_get_blocksize.argtypes = [POINTER(FLAC__StreamEncoder)]
+
+ def FLAC__stream_encoder_get_blocksize(encoder):
+ return libflac.FLAC__stream_encoder_get_blocksize(encoder)
+
+ libflac.FLAC__stream_encoder_get_do_mid_side_stereo.restype = FLAC__bool
+ libflac.FLAC__stream_encoder_get_do_mid_side_stereo.argtypes = [POINTER(FLAC__StreamEncoder)]
+
+ def FLAC__stream_encoder_get_do_mid_side_stereo(encoder):
+ return libflac.FLAC__stream_encoder_get_do_mid_side_stereo(encoder)
+
+ libflac.FLAC__stream_encoder_get_loose_mid_side_stereo.restype = FLAC__bool
+ libflac.FLAC__stream_encoder_get_loose_mid_side_stereo.argtypes = [POINTER(FLAC__StreamEncoder)]
+
+ def FLAC__stream_encoder_get_loose_mid_side_stereo(encoder):
+ return libflac.FLAC__stream_encoder_get_loose_mid_side_stereo(encoder)
+
+ libflac.FLAC__stream_encoder_get_max_lpc_order.restype = c_uint
+ libflac.FLAC__stream_encoder_get_max_lpc_order.argtypes = [POINTER(FLAC__StreamEncoder)]
+
+ def FLAC__stream_encoder_get_max_lpc_order(encoder):
+ return libflac.FLAC__stream_encoder_get_max_lpc_order(encoder)
+
+ libflac.FLAC__stream_encoder_get_qlp_coeff_precision.restype = c_uint
+ libflac.FLAC__stream_encoder_get_qlp_coeff_precision.argtypes = [POINTER(FLAC__StreamEncoder)]
+
+ def FLAC__stream_encoder_get_qlp_coeff_precision(encoder):
+ return libflac.FLAC__stream_encoder_get_qlp_coeff_precision(encoder)
+
+ libflac.FLAC__stream_encoder_get_do_qlp_coeff_prec_search.restype = FLAC__bool
+ libflac.FLAC__stream_encoder_get_do_qlp_coeff_prec_search.argtypes = [POINTER(FLAC__StreamEncoder)]
+
+ def FLAC__stream_encoder_get_do_qlp_coeff_prec_search(encoder):
+ return libflac.FLAC__stream_encoder_get_do_qlp_coeff_prec_search(encoder)
+
+ libflac.FLAC__stream_encoder_get_do_escape_coding.restype = FLAC__bool
+ libflac.FLAC__stream_encoder_get_do_escape_coding.argtypes = [POINTER(FLAC__StreamEncoder)]
+
+ def FLAC__stream_encoder_get_do_escape_coding(encoder):
+ return libflac.FLAC__stream_encoder_get_do_escape_coding(encoder)
+
+ libflac.FLAC__stream_encoder_get_do_exhaustive_model_search.restype = FLAC__bool
+ libflac.FLAC__stream_encoder_get_do_exhaustive_model_search.argtypes = [POINTER(FLAC__StreamEncoder)]
+
+ def FLAC__stream_encoder_get_do_exhaustive_model_search(encoder):
+ return libflac.FLAC__stream_encoder_get_do_exhaustive_model_search(encoder)
+
+ libflac.FLAC__stream_encoder_get_min_residual_partition_order.restype = c_uint
+ libflac.FLAC__stream_encoder_get_min_residual_partition_order.argtypes = [POINTER(FLAC__StreamEncoder)]
+
+ def FLAC__stream_encoder_get_min_residual_partition_order(encoder):
+ return libflac.FLAC__stream_encoder_get_min_residual_partition_order(encoder)
+
+ libflac.FLAC__stream_encoder_get_max_residual_partition_order.restype = c_uint
+ libflac.FLAC__stream_encoder_get_max_residual_partition_order.argtypes = [POINTER(FLAC__StreamEncoder)]
+
+ def FLAC__stream_encoder_get_max_residual_partition_order(encoder):
+ return libflac.FLAC__stream_encoder_get_max_residual_partition_order(encoder)
+
+ libflac.FLAC__stream_encoder_get_rice_parameter_search_dist.restype = c_uint
+ libflac.FLAC__stream_encoder_get_rice_parameter_search_dist.argtypes = [POINTER(FLAC__StreamEncoder)]
+
+ def FLAC__stream_encoder_get_rice_parameter_search_dist(encoder):
+ return libflac.FLAC__stream_encoder_get_rice_parameter_search_dist(encoder)
+
+ libflac.FLAC__stream_encoder_get_total_samples_estimate.restype = FLAC__uint64
+ libflac.FLAC__stream_encoder_get_total_samples_estimate.argtypes = [POINTER(FLAC__StreamEncoder)]
+
+ def FLAC__stream_encoder_get_total_samples_estimate(encoder):
+ return libflac.FLAC__stream_encoder_get_total_samples_estimate(encoder)
+
+ libflac.FLAC__stream_encoder_init_stream.restype = FLAC__StreamEncoderInitStatus
+ libflac.FLAC__stream_encoder_init_stream.argtypes = [POINTER(FLAC__StreamEncoder),
+ FLAC__StreamEncoderWriteCallback,
+ FLAC__StreamEncoderSeekCallback,
+ FLAC__StreamEncoderTellCallback,
+ FLAC__StreamEncoderMetadataCallback,
+ c_void_p]
+
+ def FLAC__stream_encoder_init_stream(encoder, write_callback, seek_callback, tell_callback, metadata_callback,client_data):
+ return libflac.FLAC__stream_encoder_init_stream(encoder, write_callback, seek_callback, tell_callback, metadata_callback,client_data)
+
+ libflac.FLAC__stream_encoder_init_ogg_stream.restype = FLAC__StreamEncoderInitStatus
+ libflac.FLAC__stream_encoder_init_ogg_stream.argtypes = [POINTER(FLAC__StreamEncoder),
+ FLAC__StreamEncoderReadCallback,
+ FLAC__StreamEncoderWriteCallback,
+ FLAC__StreamEncoderSeekCallback,
+ FLAC__StreamEncoderTellCallback,
+ FLAC__StreamEncoderMetadataCallback,
+ c_void_p]
+
+ def FLAC__stream_encoder_init_ogg_stream(encoder, read_callback, write_callback, seek_callback, tell_callback, metadata_callback,client_data):
+ return libflac.FLAC__stream_encoder_init_ogg_stream(encoder, read_callback, write_callback, seek_callback, tell_callback, metadata_callback,client_data)
+
+ libflac.FLAC__stream_encoder_init_file.restype = FLAC__StreamEncoderInitStatus
+ libflac.FLAC__stream_encoder_init_file.argtypes = [POINTER(FLAC__StreamEncoder),
+ c_char_p,
+ FLAC__StreamEncoderProgressCallback,
+ c_void_p]
+
+ def FLAC__stream_encoder_init_file(encoder, filename, progress_callback,client_data):
+ return libflac.FLAC__stream_encoder_init_file(encoder, filename, progress_callback,client_data)
+
+
+ libflac.FLAC__stream_encoder_init_ogg_file.restype = FLAC__StreamEncoderInitStatus
+ libflac.FLAC__stream_encoder_init_ogg_file.argtypes = [POINTER(FLAC__StreamEncoder),
+ c_char_p,
+ FLAC__StreamEncoderProgressCallback,
+ c_void_p]
+
+ def FLAC__stream_encoder_init_ogg_file(encoder, filename, progress_callback,client_data):
+ return libflac.FLAC__stream_encoder_init_ogg_file(encoder, filename, progress_callback,client_data)
+
+ libflac.FLAC__stream_encoder_finish.restype = FLAC__bool
+ libflac.FLAC__stream_encoder_finish.argtypes = [POINTER(FLAC__StreamEncoder)]
+
+ def FLAC__stream_encoder_finish(encoder):
+ return libflac.FLAC__stream_encoder_finish(encoder)
+
+ libflac.FLAC__stream_encoder_process.restype = FLAC__bool
+ libflac.FLAC__stream_encoder_process.argtypes = [POINTER(FLAC__StreamEncoder), POINTER(FLAC__int32_p*0), c_uint]
+
+ def FLAC__stream_encoder_process(encoder, buffer, samples):
+ return libflac.FLAC__stream_encoder_process(encoder, buffer, samples)
+
+ libflac.FLAC__stream_encoder_process_interleaved.restype = FLAC__bool
+ libflac.FLAC__stream_encoder_process_interleaved.argtypes = [POINTER(FLAC__StreamEncoder), POINTER(FLAC__int32*0), c_uint]
+
+ def FLAC__stream_encoder_process_interleaved(encoder, buffer, samples):
+ return libflac.FLAC__stream_encoder_process_interleaved(encoder, buffer, samples)
+
+ # /stream_encoder
diff --git a/sbapp/pyogg/flac_file.py b/sbapp/pyogg/flac_file.py
new file mode 100644
index 00000000..7e97ca76
--- /dev/null
+++ b/sbapp/pyogg/flac_file.py
@@ -0,0 +1,114 @@
+import ctypes
+from itertools import chain
+
+from . import flac
+from .audio_file import AudioFile
+from .pyogg_error import PyOggError
+
+def _to_char_p(string):
+ try:
+ return ctypes.c_char_p(string.encode("utf-8"))
+ except:
+ return ctypes.c_char_p(string)
+
+def _resize_array(array, new_size):
+ return (array._type_*new_size).from_address(ctypes.addressof(array))
+
+
+class FlacFile(AudioFile):
+ def write_callback(self, decoder, frame, buffer, client_data):
+ multi_channel_buf = _resize_array(buffer.contents, self.channels)
+ arr_size = frame.contents.header.blocksize
+ if frame.contents.header.channels >= 2:
+ arrays = []
+ for i in range(frame.contents.header.channels):
+ arr = ctypes.cast(multi_channel_buf[i], ctypes.POINTER(flac.FLAC__int32*arr_size)).contents
+ arrays.append(arr[:])
+
+ arr = list(chain.from_iterable(zip(*arrays)))
+
+ self.buffer[self.buffer_pos : self.buffer_pos + len(arr)] = arr[:]
+ self.buffer_pos += len(arr)
+
+ else:
+ arr = ctypes.cast(multi_channel_buf[0], ctypes.POINTER(flac.FLAC__int32*arr_size)).contents
+ self.buffer[self.buffer_pos : self.buffer_pos + arr_size] = arr[:]
+ self.buffer_pos += arr_size
+ return 0
+
+ def metadata_callback(self,decoder, metadata, client_data):
+ if not self.buffer:
+ self.total_samples = metadata.contents.data.stream_info.total_samples
+ self.channels = metadata.contents.data.stream_info.channels
+ Buffer = flac.FLAC__int16*(self.total_samples * self.channels)
+ self.buffer = Buffer()
+ self.frequency = metadata.contents.data.stream_info.sample_rate
+
+ def error_callback(self,decoder, status, client_data):
+ raise PyOggError("An error occured during the process of decoding. Status enum: {}".format(flac.FLAC__StreamDecoderErrorStatusEnum[status]))
+
+ def __init__(self, path):
+ self.decoder = flac.FLAC__stream_decoder_new()
+
+ self.client_data = ctypes.c_void_p()
+
+ #: Number of channels in audio file.
+ self.channels = None
+
+ #: Number of samples per second (per channel). For
+ # example, 44100.
+ self.frequency = None
+
+ self.total_samples = None
+
+ #: Raw PCM data from audio file.
+ self.buffer = None
+
+ self.buffer_pos = 0
+
+ write_callback_ = flac.FLAC__StreamDecoderWriteCallback(self.write_callback)
+
+ metadata_callback_ = flac.FLAC__StreamDecoderMetadataCallback(self.metadata_callback)
+
+ error_callback_ = flac.FLAC__StreamDecoderErrorCallback(self.error_callback)
+
+ init_status = flac.FLAC__stream_decoder_init_file(
+ self.decoder,
+ _to_char_p(path), # This will have an issue with Unicode filenames
+ write_callback_,
+ metadata_callback_,
+ error_callback_,
+ self.client_data
+ )
+
+ if init_status: # error
+ error = flac.FLAC__StreamDecoderInitStatusEnum[init_status]
+ raise PyOggError(
+ "An error occured when trying to open '{}': {}".format(path, error)
+ )
+
+ metadata_status = (flac.FLAC__stream_decoder_process_until_end_of_metadata(self.decoder))
+ if not metadata_status: # error
+ raise PyOggError("An error occured when trying to decode the metadata of {}".format(path))
+
+ stream_status = (flac.FLAC__stream_decoder_process_until_end_of_stream(self.decoder))
+ if not stream_status: # error
+ raise PyOggError("An error occured when trying to decode the audio stream of {}".format(path))
+
+ flac.FLAC__stream_decoder_finish(self.decoder)
+
+ #: Length of buffer
+ self.buffer_length = len(self.buffer)
+
+ self.bytes_per_sample = ctypes.sizeof(flac.FLAC__int16) # See definition of Buffer in metadata_callback()
+
+ # Cast buffer to one-dimensional array of chars
+ CharBuffer = (
+ ctypes.c_byte *
+ (self.bytes_per_sample * len(self.buffer))
+ )
+ self.buffer = CharBuffer.from_buffer(self.buffer)
+
+ # FLAC audio is always signed. See
+ # https://xiph.org/flac/api/group__flac__stream__decoder.html#gaf98a4f9e2cac5747da6018c3dfc8dde1
+ self.signed = True
diff --git a/sbapp/pyogg/flac_file_stream.py b/sbapp/pyogg/flac_file_stream.py
new file mode 100644
index 00000000..f832c312
--- /dev/null
+++ b/sbapp/pyogg/flac_file_stream.py
@@ -0,0 +1,141 @@
+import ctypes
+from itertools import chain
+
+from . import flac
+from .pyogg_error import PyOggError
+
+def _to_char_p(string):
+ try:
+ return ctypes.c_char_p(string.encode("utf-8"))
+ except:
+ return ctypes.c_char_p(string)
+
+def _resize_array(array, new_size):
+ return (array._type_*new_size).from_address(ctypes.addressof(array))
+
+
+class FlacFileStream:
+ def write_callback(self,decoder, frame, buffer, client_data):
+ multi_channel_buf = _resize_array(buffer.contents, self.channels)
+ arr_size = frame.contents.header.blocksize
+ if frame.contents.header.channels >= 2:
+ arrays = []
+ for i in range(frame.contents.header.channels):
+ arr = ctypes.cast(multi_channel_buf[i], ctypes.POINTER(flac.FLAC__int32*arr_size)).contents
+ arrays.append(arr[:])
+
+ arr = list(chain.from_iterable(zip(*arrays)))
+
+ self.buffer = (flac.FLAC__int16*len(arr))(*arr)
+ self.bytes_written = len(arr) * 2
+
+ else:
+ arr = ctypes.cast(multi_channel_buf[0], ctypes.POINTER(flac.FLAC__int32*arr_size)).contents
+ self.buffer = (flac.FLAC__int16*len(arr))(*arr[:])
+ self.bytes_written = arr_size * 2
+ return 0
+
+ def metadata_callback(self,decoder, metadata, client_data):
+ self.total_samples = metadata.contents.data.stream_info.total_samples
+ self.channels = metadata.contents.data.stream_info.channels
+ self.frequency = metadata.contents.data.stream_info.sample_rate
+
+ def error_callback(self,decoder, status, client_data):
+ raise PyOggError("An error occured during the process of decoding. Status enum: {}".format(flac.FLAC__StreamDecoderErrorStatusEnum[status]))
+
+ def __init__(self, path):
+ self.decoder = flac.FLAC__stream_decoder_new()
+
+ self.client_data = ctypes.c_void_p()
+
+ #: Number of channels in audio file.
+ self.channels = None
+
+ #: Number of samples per second (per channel). For
+ # example, 44100.
+ self.frequency = None
+
+ self.total_samples = None
+
+ self.buffer = None
+
+ self.bytes_written = None
+
+ self.write_callback_ = flac.FLAC__StreamDecoderWriteCallback(self.write_callback)
+
+ self.metadata_callback_ = flac.FLAC__StreamDecoderMetadataCallback(self.metadata_callback)
+
+ self.error_callback_ = flac.FLAC__StreamDecoderErrorCallback(self.error_callback)
+
+ init_status = flac.FLAC__stream_decoder_init_file(self.decoder,
+ _to_char_p(path),
+ self.write_callback_,
+ self.metadata_callback_,
+ self.error_callback_,
+ self.client_data)
+
+ if init_status: # error
+ raise PyOggError("An error occured when trying to open '{}': {}".format(path, flac.FLAC__StreamDecoderInitStatusEnum[init_status]))
+
+ metadata_status = (flac.FLAC__stream_decoder_process_until_end_of_metadata(self.decoder))
+ if not metadata_status: # error
+ raise PyOggError("An error occured when trying to decode the metadata of {}".format(path))
+
+ #: Bytes per sample
+ self.bytes_per_sample = 2
+
+ def get_buffer(self):
+ """Returns the buffer.
+
+ Returns buffer (a bytes object) or None if all data has
+ been read from the file.
+
+ """
+ # Attempt to read a single frame of audio
+ stream_status = (flac.FLAC__stream_decoder_process_single(self.decoder))
+ if not stream_status: # error
+ raise PyOggError("An error occured when trying to decode the audio stream of {}".format(path))
+
+ # Check if we encountered the end of the stream
+ if (flac.FLAC__stream_decoder_get_state(self.decoder) == 4): # end of stream
+ return None
+
+ buffer_as_bytes = bytes(self.buffer)
+ return buffer_as_bytes
+
+ def clean_up(self):
+ flac.FLAC__stream_decoder_finish(self.decoder)
+
+ def get_buffer_as_array(self):
+ """Provides the buffer as a NumPy array.
+
+ Note that the underlying data type is 16-bit signed
+ integers.
+
+ Does not copy the underlying data, so the returned array
+ should either be processed or copied before the next call
+ to get_buffer() or get_buffer_as_array().
+
+ """
+ import numpy # type: ignore
+
+ # Read the next samples from the stream
+ buf = self.get_buffer()
+
+ # Check if we've come to the end of the stream
+ if buf is None:
+ return None
+
+ # Convert the bytes buffer to a NumPy array
+ array = numpy.frombuffer(
+ buf,
+ dtype=numpy.int16
+ )
+
+ # Reshape the array
+ return array.reshape(
+ (len(buf)
+ // self.bytes_per_sample
+ // self.channels,
+ self.channels)
+ )
diff --git a/sbapp/pyogg/library_loader.py b/sbapp/pyogg/library_loader.py
new file mode 100644
index 00000000..711b1ba6
--- /dev/null
+++ b/sbapp/pyogg/library_loader.py
@@ -0,0 +1,147 @@
+import ctypes
+import ctypes.util
+import os
+import sys
+import platform
+from typing import (
+ Optional,
+ Dict,
+ List
+)
+
+_here = os.path.dirname(__file__)
+
+class ExternalLibraryError(Exception):
+ pass
+
+architecture = platform.architecture()[0]
+
+_windows_styles = ["{}", "lib{}", "lib{}_dynamic", "{}_dynamic"]
+
+_other_styles = ["{}", "lib{}"]
+
+if architecture == "32bit":
+ for arch_style in ["32bit", "32" "86", "win32", "x86", "_x86", "_32", "_win32", "_32bit"]:
+ for style in ["{}", "lib{}"]:
+ _windows_styles.append(style.format("{}"+arch_style))
+
+elif architecture == "64bit":
+ for arch_style in ["64bit", "64" "86_64", "amd64", "win_amd64", "x86_64", "_x86_64", "_64", "_amd64", "_64bit"]:
+ for style in ["{}", "lib{}"]:
+ _windows_styles.append(style.format("{}"+arch_style))
+
+
+run_tests = lambda lib, tests: [f(lib) for f in tests]
+
+# Get the appropriate directory for the shared libraries depending
+# on the current platform and architecture
+platform_ = platform.system()
+lib_dir = None
+if platform_ == "Darwin":
+ lib_dir = "libs/macos"
+elif platform_ == "Windows":
+ if architecture == "32bit":
+ lib_dir = "libs/win32"
+ elif architecture == "64bit":
+ lib_dir = "libs/win_amd64"
+
+
+class Library:
+ @staticmethod
+ def load(names: Dict[str, str], paths: Optional[List[str]] = None, tests = []) -> Optional[ctypes.CDLL]:
+ lib = InternalLibrary.load(names, tests)
+ if lib is None:
+ lib = ExternalLibrary.load(names["external"], paths, tests)
+ return lib
+
+
+class InternalLibrary:
+ @staticmethod
+ def load(names: Dict[str, str], tests) -> Optional[ctypes.CDLL]:
+ # If we do not have a library directory, give up immediately
+ if lib_dir is None:
+ return None
+
+ # Get the appropriate library filename given the platform
+ try:
+ name = names[platform_]
+ except KeyError:
+ return None
+
+ # Attempt to load the library from here
+ path = _here + "/" + lib_dir + "/" + name
+ try:
+ lib = ctypes.CDLL(path)
+ except OSError as e:
+ return None
+
+ # Check that the library passes the tests
+ if tests and all(run_tests(lib, tests)):
+ return lib
+
+ # Library failed tests
+ return None
+
+# Cache of libraries that have already been loaded
+_loaded_libraries: Dict[str, ctypes.CDLL] = {}
+
+class ExternalLibrary:
+ @staticmethod
+ def load(name, paths = None, tests = []):
+ if name in _loaded_libraries:
+ return _loaded_libraries[name]
+ if sys.platform == "win32":
+ lib = ExternalLibrary.load_windows(name, paths, tests)
+ _loaded_libraries[name] = lib
+ return lib
+ else:
+ lib = ExternalLibrary.load_other(name, paths, tests)
+ _loaded_libraries[name] = lib
+ return lib
+
+ @staticmethod
+ def load_other(name, paths = None, tests = []):
+ os.environ["PATH"] += ";" + ";".join((os.getcwd(), _here))
+ if paths: os.environ["PATH"] += ";" + ";".join(paths)
+
+ for style in _other_styles:
+ candidate = style.format(name)
+ library = ctypes.util.find_library(candidate)
+ if library:
+ try:
+ lib = ctypes.CDLL(library)
+ if tests and all(run_tests(lib, tests)):
+ return lib
+ except:
+ pass
+
+ @staticmethod
+ def load_windows(name, paths = None, tests = []):
+ os.environ["PATH"] += ";" + ";".join((os.getcwd(), _here))
+ if paths: os.environ["PATH"] += ";" + ";".join(paths)
+
+ not_supported = [] # libraries that were found, but are not supported
+ for style in _windows_styles:
+ candidate = style.format(name)
+ library = ctypes.util.find_library(candidate)
+ if library:
+ try:
+ lib = ctypes.CDLL(library)
+ if tests and all(run_tests(lib, tests)):
+ return lib
+ not_supported.append(library)
+ except WindowsError:
+ pass
+ except OSError:
+ not_supported.append(library)
+
+
+ if not_supported:
+ raise ExternalLibraryError("library '{}' couldn't be loaded, because the following candidates were not supported:".format(name)
+ + ("\n{}" * len(not_supported)).format(*not_supported))
+
+ raise ExternalLibraryError("library '{}' couldn't be loaded".format(name))
+
+
+
+
diff --git a/sbapp/pyogg/ogg.py b/sbapp/pyogg/ogg.py
new file mode 100644
index 00000000..08a944b4
--- /dev/null
+++ b/sbapp/pyogg/ogg.py
@@ -0,0 +1,672 @@
+############################################################
+# Ogg license: #
+############################################################
+"""
+Copyright (c) 2002, Xiph.org Foundation
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+- Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+- Neither the name of the Xiph.org Foundation nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""
+
+import ctypes
+from ctypes import c_int, c_int8, c_int16, c_int32, c_int64, c_uint, c_uint8, c_uint16, c_uint32, c_uint64, c_float, c_long, c_ulong, c_char, c_char_p, c_ubyte, c_longlong, c_ulonglong, c_size_t, c_void_p, c_double, POINTER, pointer, cast
+import ctypes.util
+import sys
+from traceback import print_exc as _print_exc
+import os
+
+from .library_loader import Library, ExternalLibrary, ExternalLibraryError
+
+
+def get_raw_libname(name):
+ name = os.path.splitext(name)[0].lower()
+ for x in "0123456789._- ":name=name.replace(x,"")
+ return name
+
+# Define a function to convert strings to char-pointers. In Python 3
+# all strings are Unicode, while in Python 2 they were ASCII-encoded.
+# FIXME: Does PyOgg even support Python 2?
+if sys.version_info.major > 2:
+ to_char_p = lambda s: s.encode('utf-8')
+else:
+ to_char_p = lambda s: s
+
+__here = os.getcwd()
+
+libogg = None
+
+try:
+ names = {
+ "Windows": "ogg.dll",
+ "Darwin": "libogg.0.dylib",
+ "external": "ogg"
+ }
+ libogg = Library.load(names, tests = [lambda lib: hasattr(lib, "oggpack_writeinit")])
+except ExternalLibraryError:
+ pass
+except:
+ _print_exc()
+
+if libogg is not None:
+ PYOGG_OGG_AVAIL = True
+else:
+ PYOGG_OGG_AVAIL = False
+
+if PYOGG_OGG_AVAIL:
+ # Sanity check also satisfies mypy type checking
+ assert libogg is not None
+
+ # ctypes
+ c_ubyte_p = POINTER(c_ubyte)
+ c_uchar = c_ubyte
+ c_uchar_p = c_ubyte_p
+ c_float_p = POINTER(c_float)
+ c_float_p_p = POINTER(c_float_p)
+ c_float_p_p_p = POINTER(c_float_p_p)
+ c_char_p_p = POINTER(c_char_p)
+ c_int_p = POINTER(c_int)
+ c_long_p = POINTER(c_long)
+
+ # os_types
+ ogg_int16_t = c_int16
+ ogg_uint16_t = c_uint16
+ ogg_int32_t = c_int32
+ ogg_uint32_t = c_uint32
+ ogg_int64_t = c_int64
+ ogg_uint64_t = c_uint64
+ ogg_int64_t_p = POINTER(ogg_int64_t)
+
+ # ogg
+ class ogg_iovec_t(ctypes.Structure):
+ """
+ Wrapper for:
+ typedef struct ogg_iovec_t;
+ """
+ _fields_ = [("iov_base", c_void_p),
+ ("iov_len", c_size_t)]
+
+ class oggpack_buffer(ctypes.Structure):
+ """
+ Wrapper for:
+ typedef struct oggpack_buffer;
+ """
+ _fields_ = [("endbyte", c_long),
+ ("endbit", c_int),
+ ("buffer", c_uchar_p),
+ ("ptr", c_uchar_p),
+ ("storage", c_long)]
+
+ class ogg_page(ctypes.Structure):
+ """
+ Wrapper for:
+ typedef struct ogg_page;
+ """
+ _fields_ = [("header", c_uchar_p),
+ ("header_len", c_long),
+ ("body", c_uchar_p),
+ ("body_len", c_long)]
+
+ class ogg_stream_state(ctypes.Structure):
+ """
+ Wrapper for:
+ typedef struct ogg_stream_state;
+ """
+ _fields_ = [("body_data", c_uchar_p),
+ ("body_storage", c_long),
+ ("body_fill", c_long),
+ ("body_returned", c_long),
+
+ ("lacing_vals", c_int),
+ ("granule_vals", ogg_int64_t),
+
+ ("lacing_storage", c_long),
+ ("lacing_fill", c_long),
+ ("lacing_packet", c_long),
+ ("lacing_returned", c_long),
+
+ ("header", c_uchar*282),
+ ("header_fill", c_int),
+
+ ("e_o_s", c_int),
+ ("b_o_s", c_int),
+
+ ("serialno", c_long),
+ ("pageno", c_long),
+ ("packetno", ogg_int64_t),
+ ("granulepos", ogg_int64_t)]
+
+ class ogg_packet(ctypes.Structure):
+ """
+ Wrapper for:
+ typedef struct ogg_packet;
+ """
+ _fields_ = [("packet", c_uchar_p),
+ ("bytes", c_long),
+ ("b_o_s", c_long),
+ ("e_o_s", c_long),
+
+ ("granulepos", ogg_int64_t),
+
+ ("packetno", ogg_int64_t)]
+
+ def __str__(self):
+ bos = ""
+ if self.b_o_s:
+ bos = "beginning of stream, "
+ eos = ""
+ if self.e_o_s:
+ eos = "end of stream, "
+
+ # Converting the data will cause a seg-fault if the memory isn't valid
+ data = bytes(self.packet[0:self.bytes])
+ value = (
+ f"Ogg Packet <{hex(id(self))}>: " +
+ f"number {self.packetno}, " +
+ f"granule position {self.granulepos}, " +
+ bos + eos +
+ f"{self.bytes} bytes"
+ )
+ return value
+
+ class ogg_sync_state(ctypes.Structure):
+ """
+ Wrapper for:
+ typedef struct ogg_sync_state;
+ """
+ _fields_ = [("data", c_uchar_p),
+ ("storage", c_int),
+ ("fill", c_int),
+ ("returned", c_int),
+
+ ("unsynched", c_int),
+ ("headerbytes", c_int),
+ ("bodybytes", c_int)]
+
+ b_p = POINTER(oggpack_buffer)
+ oy_p = POINTER(ogg_sync_state)
+ op_p = POINTER(ogg_packet)
+ og_p = POINTER(ogg_page)
+ os_p = POINTER(ogg_stream_state)
+ iov_p = POINTER(ogg_iovec_t)
+
+ libogg.oggpack_writeinit.restype = None
+ libogg.oggpack_writeinit.argtypes = [b_p]
+
+ def oggpack_writeinit(b):
+ libogg.oggpack_writeinit(b)
+
+ try:
+ libogg.oggpack_writecheck.restype = c_int
+ libogg.oggpack_writecheck.argtypes = [b_p]
+ def oggpack_writecheck(b):
+ libogg.oggpack_writecheck(b)
+ except:
+ pass
+
+ libogg.oggpack_writetrunc.restype = None
+ libogg.oggpack_writetrunc.argtypes = [b_p, c_long]
+
+ def oggpack_writetrunc(b, bits):
+ libogg.oggpack_writetrunc(b, bits)
+
+ libogg.oggpack_writealign.restype = None
+ libogg.oggpack_writealign.argtypes = [b_p]
+
+ def oggpack_writealign(b):
+ libogg.oggpack_writealign(b)
+
+ libogg.oggpack_writecopy.restype = None
+ libogg.oggpack_writecopy.argtypes = [b_p, c_void_p, c_long]
+
+ def oggpack_writecopy(b, source, bits):
+ libogg.oggpack_writecopy(b, source, bits)
+
+ libogg.oggpack_reset.restype = None
+ libogg.oggpack_reset.argtypes = [b_p]
+
+ def oggpack_reset(b):
+ libogg.oggpack_reset(b)
+
+ libogg.oggpack_writeclear.restype = None
+ libogg.oggpack_writeclear.argtypes = [b_p]
+
+ def oggpack_writeclear(b):
+ libogg.oggpack_writeclear(b)
+
+ libogg.oggpack_readinit.restype = None
+ libogg.oggpack_readinit.argtypes = [b_p, c_uchar_p, c_int]
+
+ def oggpack_readinit(b, buf, bytes):
+ libogg.oggpack_readinit(b, buf, bytes)
+
+ libogg.oggpack_write.restype = None
+ libogg.oggpack_write.argtypes = [b_p, c_ulong, c_int]
+
+ def oggpack_write(b, value, bits):
+ libogg.oggpack_write(b, value, bits)
+
+ libogg.oggpack_look.restype = c_long
+ libogg.oggpack_look.argtypes = [b_p, c_int]
+
+ def oggpack_look(b, bits):
+ return libogg.oggpack_look(b, bits)
+
+ libogg.oggpack_look1.restype = c_long
+ libogg.oggpack_look1.argtypes = [b_p]
+
+ def oggpack_look1(b):
+ return libogg.oggpack_look1(b)
+
+ libogg.oggpack_adv.restype = None
+ libogg.oggpack_adv.argtypes = [b_p, c_int]
+
+ def oggpack_adv(b, bits):
+ libogg.oggpack_adv(b, bits)
+
+ libogg.oggpack_adv1.restype = None
+ libogg.oggpack_adv1.argtypes = [b_p]
+
+ def oggpack_adv1(b):
+ libogg.oggpack_adv1(b)
+
+ libogg.oggpack_read.restype = c_long
+ libogg.oggpack_read.argtypes = [b_p, c_int]
+
+ def oggpack_read(b, bits):
+ return libogg.oggpack_read(b, bits)
+
+ libogg.oggpack_read1.restype = c_long
+ libogg.oggpack_read1.argtypes = [b_p]
+
+ def oggpack_read1(b):
+ return libogg.oggpack_read1(b)
+
+ libogg.oggpack_bytes.restype = c_long
+ libogg.oggpack_bytes.argtypes = [b_p]
+
+ def oggpack_bytes(b):
+ return libogg.oggpack_bytes(b)
+
+ libogg.oggpack_bits.restype = c_long
+ libogg.oggpack_bits.argtypes = [b_p]
+
+ def oggpack_bits(b):
+ return libogg.oggpack_bits(b)
+
+ libogg.oggpack_get_buffer.restype = c_uchar_p
+ libogg.oggpack_get_buffer.argtypes = [b_p]
+
+ def oggpack_get_buffer(b):
+ return libogg.oggpack_get_buffer(b)
+
+
+
+ libogg.oggpackB_writeinit.restype = None
+ libogg.oggpackB_writeinit.argtypes = [b_p]
+
+ def oggpackB_writeinit(b):
+ libogg.oggpackB_writeinit(b)
+
+ try:
+ libogg.oggpackB_writecheck.restype = c_int
+ libogg.oggpackB_writecheck.argtypes = [b_p]
+
+ def oggpackB_writecheck(b):
+ return libogg.oggpackB_writecheck(b)
+ except:
+ pass
+
+ libogg.oggpackB_writetrunc.restype = None
+ libogg.oggpackB_writetrunc.argtypes = [b_p, c_long]
+
+ def oggpackB_writetrunc(b, bits):
+ libogg.oggpackB_writetrunc(b, bits)
+
+ libogg.oggpackB_writealign.restype = None
+ libogg.oggpackB_writealign.argtypes = [b_p]
+
+ def oggpackB_writealign(b):
+ libogg.oggpackB_writealign(b)
+
+ libogg.oggpackB_writecopy.restype = None
+ libogg.oggpackB_writecopy.argtypes = [b_p, c_void_p, c_long]
+
+ def oggpackB_writecopy(b, source, bits):
+ libogg.oggpackB_writecopy(b, source, bits)
+
+ libogg.oggpackB_reset.restype = None
+ libogg.oggpackB_reset.argtypes = [b_p]
+
+ def oggpackB_reset(b):
+ libogg.oggpackB_reset(b)
+
+ libogg.oggpackB_reset.restype = None
+ libogg.oggpackB_writeclear.argtypes = [b_p]
+
+ def oggpackB_reset(b):
+ libogg.oggpackB_reset(b)
+
+ libogg.oggpackB_readinit.restype = None
+ libogg.oggpackB_readinit.argtypes = [b_p, c_uchar_p, c_int]
+
+ def oggpackB_readinit(b, buf, bytes):
+ libogg.oggpackB_readinit(b, buf, bytes)
+
+ libogg.oggpackB_write.restype = None
+ libogg.oggpackB_write.argtypes = [b_p, c_ulong, c_int]
+
+ def oggpackB_write(b, value, bits):
+ libogg.oggpackB_write(b, value, bits)
+
+ libogg.oggpackB_look.restype = c_long
+ libogg.oggpackB_look.argtypes = [b_p, c_int]
+
+ def oggpackB_look(b, bits):
+ return libogg.oggpackB_look(b, bits)
+
+ libogg.oggpackB_look1.restype = c_long
+ libogg.oggpackB_look1.argtypes = [b_p]
+
+ def oggpackB_look1(b):
+ return libogg.oggpackB_look1(b)
+
+ libogg.oggpackB_adv.restype = None
+ libogg.oggpackB_adv.argtypes = [b_p, c_int]
+
+ def oggpackB_adv(b, bits):
+ libogg.oggpackB_adv(b, bits)
+
+ libogg.oggpackB_adv1.restype = None
+ libogg.oggpackB_adv1.argtypes = [b_p]
+
+ def oggpackB_adv1(b):
+ libogg.oggpackB_adv1(b)
+
+ libogg.oggpackB_read.restype = c_long
+ libogg.oggpackB_read.argtypes = [b_p, c_int]
+
+ def oggpackB_read(b, bits):
+ return libogg.oggpackB_read(b, bits)
+
+ libogg.oggpackB_read1.restype = c_long
+ libogg.oggpackB_read1.argtypes = [b_p]
+
+ def oggpackB_read1(b):
+ return libogg.oggpackB_read1(b)
+
+ libogg.oggpackB_bytes.restype = c_long
+ libogg.oggpackB_bytes.argtypes = [b_p]
+
+ def oggpackB_bytes(b):
+ return libogg.oggpackB_bytes(b)
+
+ libogg.oggpackB_bits.restype = c_long
+ libogg.oggpackB_bits.argtypes = [b_p]
+
+ def oggpackB_bits(b):
+ return libogg.oggpackB_bits(b)
+
+ libogg.oggpackB_get_buffer.restype = c_uchar_p
+ libogg.oggpackB_get_buffer.argtypes = [b_p]
+
+ def oggpackB_get_buffer(b):
+ return libogg.oggpackB_get_buffer(b)
+
+
+
+ libogg.ogg_stream_packetin.restype = c_int
+ libogg.ogg_stream_packetin.argtypes = [os_p, op_p]
+
+ def ogg_stream_packetin(os, op):
+ return libogg.ogg_stream_packetin(os, op)
+
+ try:
+ libogg.ogg_stream_iovecin.restype = c_int
+ libogg.ogg_stream_iovecin.argtypes = [os_p, iov_p, c_int, c_long, ogg_int64_t]
+
+ def ogg_stream_iovecin(os, iov, count, e_o_s, granulepos):
+ return libogg.ogg_stream_iovecin(os, iov, count, e_o_s, granulepos)
+ except:
+ pass
+
+ libogg.ogg_stream_pageout.restype = c_int
+ libogg.ogg_stream_pageout.argtypes = [os_p, og_p]
+
+ def ogg_stream_pageout(os, og):
+ return libogg.ogg_stream_pageout(os, og)
+
+ try:
+ libogg.ogg_stream_pageout_fill.restype = c_int
+ libogg.ogg_stream_pageout_fill.argtypes = [os_p, og_p, c_int]
+ def ogg_stream_pageout_fill(os, og, nfill):
+ return libogg.ogg_stream_pageout_fill(os, og, nfill)
+ except:
+ pass
+
+ libogg.ogg_stream_flush.restype = c_int
+ libogg.ogg_stream_flush.argtypes = [os_p, og_p]
+
+ def ogg_stream_flush(os, og):
+ return libogg.ogg_stream_flush(os, og)
+
+ try:
+ libogg.ogg_stream_flush_fill.restype = c_int
+ libogg.ogg_stream_flush_fill.argtypes = [os_p, og_p, c_int]
+ def ogg_stream_flush_fill(os, og, nfill):
+ return libogg.ogg_stream_flush_fill(os, og, nfill)
+ except:
+ pass
+
+
+
+ libogg.ogg_sync_init.restype = c_int
+ libogg.ogg_sync_init.argtypes = [oy_p]
+
+ def ogg_sync_init(oy):
+ return libogg.ogg_sync_init(oy)
+
+ libogg.ogg_sync_clear.restype = c_int
+ libogg.ogg_sync_clear.argtypes = [oy_p]
+
+ def ogg_sync_clear(oy):
+ return libogg.ogg_sync_clear(oy)
+
+ libogg.ogg_sync_reset.restype = c_int
+ libogg.ogg_sync_reset.argtypes = [oy_p]
+
+ def ogg_sync_reset(oy):
+ return libogg.ogg_sync_reset(oy)
+
+ libogg.ogg_sync_destroy.restype = c_int
+ libogg.ogg_sync_destroy.argtypes = [oy_p]
+
+ def ogg_sync_destroy(oy):
+ return libogg.ogg_sync_destroy(oy)
+
+ try:
+ libogg.ogg_sync_check.restype = c_int
+ libogg.ogg_sync_check.argtypes = [oy_p]
+ def ogg_sync_check(oy):
+ return libogg.ogg_sync_check(oy)
+ except:
+ pass
+
+
+
+ libogg.ogg_sync_buffer.restype = c_char_p
+ libogg.ogg_sync_buffer.argtypes = [oy_p, c_long]
+
+ def ogg_sync_buffer(oy, size):
+ return libogg.ogg_sync_buffer(oy, size)
+
+ libogg.ogg_sync_wrote.restype = c_int
+ libogg.ogg_sync_wrote.argtypes = [oy_p, c_long]
+
+ def ogg_sync_wrote(oy, bytes):
+ return libogg.ogg_sync_wrote(oy, bytes)
+
+ libogg.ogg_sync_pageseek.restype = c_int
+ libogg.ogg_sync_pageseek.argtypes = [oy_p, og_p]
+
+ def ogg_sync_pageseek(oy, og):
+ return libogg.ogg_sync_pageseek(oy, og)
+
+ libogg.ogg_sync_pageout.restype = c_long
+ libogg.ogg_sync_pageout.argtypes = [oy_p, og_p]
+
+ def ogg_sync_pageout(oy, og):
+ return libogg.ogg_sync_pageout(oy, og)
+
+ libogg.ogg_stream_pagein.restype = c_int
+ libogg.ogg_stream_pagein.argtypes = [os_p, og_p]
+
+ def ogg_stream_pagein(os, og):
+ return libogg.ogg_stream_pagein(oy, og)
+
+ libogg.ogg_stream_packetout.restype = c_int
+ libogg.ogg_stream_packetout.argtypes = [os_p, op_p]
+
+ def ogg_stream_packetout(os, op):
+ return libogg.ogg_stream_packetout(oy, op)
+
+ libogg.ogg_stream_packetpeek.restype = c_int
+ libogg.ogg_stream_packetpeek.argtypes = [os_p, op_p]
+
+ def ogg_stream_packetpeek(os, op):
+ return libogg.ogg_stream_packetpeek(os, op)
+
+
+
+ libogg.ogg_stream_init.restype = c_int
+ libogg.ogg_stream_init.argtypes = [os_p, c_int]
+
+ def ogg_stream_init(os, serialno):
+ return libogg.ogg_stream_init(os, serialno)
+
+ libogg.ogg_stream_clear.restype = c_int
+ libogg.ogg_stream_clear.argtypes = [os_p]
+
+ def ogg_stream_clear(os):
+ return libogg.ogg_stream_clear(os)
+
+ libogg.ogg_stream_reset.restype = c_int
+ libogg.ogg_stream_reset.argtypes = [os_p]
+
+ def ogg_stream_reset(os):
+ return libogg.ogg_stream_reset(os)
+
+ libogg.ogg_stream_reset_serialno.restype = c_int
+ libogg.ogg_stream_reset_serialno.argtypes = [os_p, c_int]
+
+ def ogg_stream_reset_serialno(os, serialno):
+ return libogg.ogg_stream_reset_serialno(os, serialno)
+
+ libogg.ogg_stream_destroy.restype = c_int
+ libogg.ogg_stream_destroy.argtypes = [os_p]
+
+ def ogg_stream_destroy(os):
+ return libogg.ogg_stream_destroy(os)
+
+ try:
+ libogg.ogg_stream_check.restype = c_int
+ libogg.ogg_stream_check.argtypes = [os_p]
+ def ogg_stream_check(os):
+ return libogg.ogg_stream_check(os)
+ except:
+ pass
+
+ libogg.ogg_stream_eos.restype = c_int
+ libogg.ogg_stream_eos.argtypes = [os_p]
+
+ def ogg_stream_eos(os):
+ return libogg.ogg_stream_eos(os)
+
+
+
+ libogg.ogg_page_checksum_set.restype = None
+ libogg.ogg_page_checksum_set.argtypes = [og_p]
+
+ def ogg_page_checksum_set(og):
+ libogg.ogg_page_checksum_set(og)
+
+
+
+ libogg.ogg_page_version.restype = c_int
+ libogg.ogg_page_version.argtypes = [og_p]
+
+ def ogg_page_version(og):
+ return libogg.ogg_page_version(og)
+
+ libogg.ogg_page_continued.restype = c_int
+ libogg.ogg_page_continued.argtypes = [og_p]
+
+ def ogg_page_continued(og):
+ return libogg.ogg_page_continued(og)
+
+ libogg.ogg_page_bos.restype = c_int
+ libogg.ogg_page_bos.argtypes = [og_p]
+
+ def ogg_page_bos(og):
+ return libogg.ogg_page_bos(og)
+
+ libogg.ogg_page_eos.restype = c_int
+ libogg.ogg_page_eos.argtypes = [og_p]
+
+ def ogg_page_eos(og):
+ return libogg.ogg_page_eos(og)
+
+ libogg.ogg_page_granulepos.restype = ogg_int64_t
+ libogg.ogg_page_granulepos.argtypes = [og_p]
+
+ def ogg_page_granulepos(og):
+ return libogg.ogg_page_granulepos(og)
+
+ libogg.ogg_page_serialno.restype = c_int
+ libogg.ogg_page_serialno.argtypes = [og_p]
+
+ def ogg_page_serialno(og):
+ return libogg.ogg_page_serialno(og)
+
+ libogg.ogg_page_pageno.restype = c_long
+ libogg.ogg_page_pageno.argtypes = [og_p]
+
+ def ogg_page_pageno(og):
+ return libogg.ogg_page_pageno(og)
+
+ libogg.ogg_page_packets.restype = c_int
+ libogg.ogg_page_packets.argtypes = [og_p]
+
+ def ogg_page_packets(og):
+ return libogg.ogg_page_packets(og)
+
+
+
+ libogg.ogg_packet_clear.restype = None
+ libogg.ogg_packet_clear.argtypes = [op_p]
+
+ def ogg_packet_clear(op):
+ libogg.ogg_packet_clear(op)
diff --git a/sbapp/pyogg/ogg_opus_writer.py b/sbapp/pyogg/ogg_opus_writer.py
new file mode 100644
index 00000000..547d0f5a
--- /dev/null
+++ b/sbapp/pyogg/ogg_opus_writer.py
@@ -0,0 +1,421 @@
+import builtins
+import copy
+import ctypes
+import random
+import struct
+from typing import (
+ Optional,
+ Union,
+ BinaryIO
+)
+
+from . import ogg
+from . import opus
+from .opus_buffered_encoder import OpusBufferedEncoder
+#from .opus_encoder import OpusEncoder
+from .pyogg_error import PyOggError
+
+class OggOpusWriter():
+ """Encodes PCM data into an OggOpus file."""
+
+ def __init__(self,
+ f: Union[BinaryIO, str],
+ encoder: OpusBufferedEncoder,
+ custom_pre_skip: Optional[int] = None) -> None:
+ """Construct an OggOpusWriter.
+
+ f may be either a string giving the path to the file, or
+ an already-opened file handle.
+
+ If f is an already-opened file handle, then it is the
+ user's responsibility to close the file when they are
+ finished with it. The file should be opened for writing
+ in binary (not text) mode.
+
+ The encoder should be a
+ OpusBufferedEncoder and should be fully configured before the
+ first call to the `write()` method.
+
+ The Opus encoder requires an amount of "warm up" and when
+ stored in an Ogg container that warm up can be skipped. When
+ `custom_pre_skip` is None, the required amount of warm up
+ silence is automatically calculated and inserted. If a custom
+ (non-silent) pre-skip is desired, then `custom_pre_skip`
+ should be specified as the number of samples (per channel).
+ It is then the user's responsibility to pass the non-silent
+ pre-skip samples to `encode()`.
+
+ """
+ # Store the Opus encoder
+ self._encoder = encoder
+
+ # Store the custom pre skip
+ self._custom_pre_skip = custom_pre_skip
+
+ # Create a new stream state with a random serial number
+ self._stream_state = self._create_stream_state()
+
+ # Create a packet (reused for each pass)
+ self._ogg_packet = ogg.ogg_packet()
+ self._packet_valid = False
+
+ # Create a page (reused for each pass)
+ self._ogg_page = ogg.ogg_page()
+
+ # Counter for the number of packets written into Ogg stream
+ self._count_packets = 0
+
+ # Counter for the number of samples encoded into Opus
+ # packets
+ self._count_samples = 0
+
+ # Flag to indicate if the headers have been written
+ self._headers_written = False
+
+ # Flag to indicate that the stream has been finished (the
+ # EOS bit was set in a final packet)
+ self._finished = False
+
+ # Reference to the current encoded packet (written only
+ # when we know if it the last)
+ self._current_encoded_packet: Optional[bytes] = None
+
+ # Open file if required. Given this may raise an exception,
+ # it should be the last step of initialisation.
+ self._i_opened_the_file = False
+ if isinstance(f, str):
+ self._file = builtins.open(f, 'wb')
+ self._i_opened_the_file = True
+ else:
+ # Assume it's already opened file
+ self._file = f
+
+ def __del__(self) -> None:
+ if not self._finished:
+ self.close()
+
+ #
+ # User visible methods
+ #
+
+ def write(self, pcm: memoryview) -> None:
+ """Encode the PCM and write out the Ogg Opus stream.
+
+ Encoders the PCM using the provided encoder.
+
+ """
+ # Check that the stream hasn't already been finished
+ if self._finished:
+ raise PyOggError(
+ "Stream has already ended. Perhaps close() was "+
+ "called too early?")
+
+ # If we haven't already written out the headers, do so
+ # now. Then, write a frame of silence to warm up the
+ # encoder.
+ if not self._headers_written:
+ pre_skip = self._write_headers(self._custom_pre_skip)
+ if self._custom_pre_skip is None:
+ self._write_silence(pre_skip)
+
+ # Call the internal method to encode the bytes
+ self._write_to_oggopus(pcm)
+
+
+ def _write_to_oggopus(self, pcm: memoryview, flush: bool = False) -> None:
+ assert self._encoder is not None
+
+ def handle_encoded_packet(encoded_packet: memoryview,
+ samples: int,
+ end_of_stream: bool) -> None:
+ # Cast memoryview to ctypes Array
+ Buffer = ctypes.c_ubyte * len(encoded_packet)
+ encoded_packet_ctypes = Buffer.from_buffer(encoded_packet)
+
+ # Obtain a pointer to the encoded packet
+ encoded_packet_ptr = ctypes.cast(
+ encoded_packet_ctypes,
+ ctypes.POINTER(ctypes.c_ubyte)
+ )
+
+ # Increase the count of the number of samples written
+ self._count_samples += samples
+
+ # Place data into the packet
+ self._ogg_packet.packet = encoded_packet_ptr
+ self._ogg_packet.bytes = len(encoded_packet)
+ self._ogg_packet.b_o_s = 0
+ self._ogg_packet.e_o_s = end_of_stream
+ self._ogg_packet.granulepos = self._count_samples
+ self._ogg_packet.packetno = self._count_packets
+
+ # Increase the counter of the number of packets
+ # in the stream
+ self._count_packets += 1
+
+ # Write the packet into the stream
+ self._write_packet()
+
+
+ # Encode the PCM data into an Opus packet
+ self._encoder.buffered_encode(
+ pcm,
+ flush=flush,
+ callback=handle_encoded_packet
+ )
+
+ def close(self) -> None:
+ # Check we haven't already closed this stream
+ if self._finished:
+ # We're attempting to close an already closed stream,
+ # do nothing more.
+ return
+
+ # Flush the underlying buffered encoder
+ self._write_to_oggopus(memoryview(bytearray(b"")), flush=True)
+
+ # The current packet must be the end of the stream, update
+ # the packet's details
+ self._ogg_packet.e_o_s = 1
+
+ # Write the packet to the stream
+ if self._packet_valid:
+ self._write_packet()
+
+ # Flush the stream of any unwritten pages
+ self._flush()
+
+ # Mark the stream as finished
+ self._finished = True
+
+ # Close the file if we opened it
+ if self._i_opened_the_file:
+ self._file.close()
+ self._i_opened_the_file = False
+
+ # Clean up the Ogg-related memory
+ ogg.ogg_stream_clear(self._stream_state)
+
+ # Clean up the reference to the encoded packet (as it must
+ # now have been written)
+ del self._current_encoded_packet
+
+ #
+ # Internal methods
+ #
+
+ def _create_random_serial_no(self) -> ctypes.c_int:
+ sizeof_c_int = ctypes.sizeof(ctypes.c_int)
+ min_int = -2**(sizeof_c_int*8-1)
+ max_int = 2**(sizeof_c_int*8-1)-1
+ serial_no = ctypes.c_int(random.randint(min_int, max_int))
+
+ return serial_no
+
+ def _create_stream_state(self) -> ogg.ogg_stream_state:
+ # Create a random serial number
+ serial_no = self._create_random_serial_no()
+
+ # Create an ogg_stream_state
+ ogg_stream_state = ogg.ogg_stream_state()
+
+ # Initialise the stream state
+ ogg.ogg_stream_init(
+ ctypes.pointer(ogg_stream_state),
+ serial_no
+ )
+
+ return ogg_stream_state
+
+ def _make_identification_header(self, pre_skip: int, input_sampling_rate: int = 0) -> bytes:
+ """Make the OggOpus identification header.
+
+ An input_sampling rate may be set to zero to mean 'unspecified'.
+
+ Only channel mapping family 0 is currently supported.
+ This allows mono and stereo signals.
+
+ See https://tools.ietf.org/html/rfc7845#page-12 for more
+ details.
+
+ """
+ signature = b"OpusHead"
+ version = 1
+ output_channels = self._encoder._channels
+ output_gain = 0
+ channel_mapping_family = 0
+ data = struct.pack(
+ "<BBHIHB",
+ version,
+ output_channels,
+ pre_skip,
+ input_sampling_rate,
+ output_gain,
+ channel_mapping_family
+ )
+
+ return signature+data
+
+ def _write_identification_header_packet(self, custom_pre_skip: int) -> int:
+ """ Returns pre-skip. """
+ if custom_pre_skip is not None:
+ # Use the user-specified amount of pre-skip
+ pre_skip = custom_pre_skip
+ else:
+ # Obtain the algorithmic delay of the Opus encoder. See
+ # https://tools.ietf.org/html/rfc7845#page-27
+ delay_samples = self._encoder.get_algorithmic_delay()
+
+ # Extra samples are recommended. See
+ # https://tools.ietf.org/html/rfc7845#page-27
+ extra_samples = 120
+
+ # We will just fill a whole frame with silence. Calculate
+ # the minimum frame length, which we'll use as the
+ # pre-skip.
+ frame_durations = [2.5, 5, 10, 20, 40, 60] # milliseconds
+ frame_lengths = [
+ x * self._encoder._samples_per_second // 1000
+ for x in frame_durations
+ ]
+ for frame_length in frame_lengths:
+ if frame_length > delay_samples + extra_samples:
+ pre_skip = frame_length
+ break
+
+ # Create the identification header
+ id_header = self._make_identification_header(
+ pre_skip = pre_skip
+ )
+
+ # Specify the packet containing the identification header
+ self._ogg_packet.packet = ctypes.cast(id_header, ogg.c_uchar_p) # type: ignore
+ self._ogg_packet.bytes = len(id_header)
+ self._ogg_packet.b_o_s = 1
+ self._ogg_packet.e_o_s = 0
+ self._ogg_packet.granulepos = 0
+ self._ogg_packet.packetno = self._count_packets
+ self._count_packets += 1
+
+ # Write the identification header
+ result = ogg.ogg_stream_packetin(
+ self._stream_state,
+ self._ogg_packet
+ )
+
+ if result != 0:
+ raise PyOggError(
+ "Failed to write Opus identification header"
+ )
+
+ return pre_skip
+
+ def _make_comment_header(self):
+ """Make the OggOpus comment header.
+
+ See https://tools.ietf.org/html/rfc7845#page-22 for more
+ details.
+
+ """
+ signature = b"OpusTags"
+ vendor_string = b"ENCODER=PyOgg"
+ vendor_string_length = struct.pack("<I",len(vendor_string))
+ user_comments_length = struct.pack("<I",0)
+
+ return (
+ signature
+ + vendor_string_length
+ + vendor_string
+ + user_comments_length
+ )
+
+ def _write_comment_header_packet(self):
+ # Specify the comment header
+ comment_header = self._make_comment_header()
+
+ # Specify the packet containing the identification header
+ self._ogg_packet.packet = ctypes.cast(comment_header, ogg.c_uchar_p)
+ self._ogg_packet.bytes = len(comment_header)
+ self._ogg_packet.b_o_s = 0
+ self._ogg_packet.e_o_s = 0
+ self._ogg_packet.granulepos = 0
+ self._ogg_packet.packetno = self._count_packets
+ self._count_packets += 1
+
+ # Write the header
+ result = ogg.ogg_stream_packetin(
+ self._stream_state,
+ self._ogg_packet
+ )
+
+ if result != 0:
+ raise PyOggError(
+ "Failed to write Opus comment header"
+ )
+
+ def _write_page(self):
+ """ Write page to file """
+ # Cast pointer to ctypes array, which can then be passed to
+ # write without issues.
+ HeaderBufferPtr = ctypes.POINTER(ctypes.c_ubyte * self._ogg_page.header_len)
+ header = HeaderBufferPtr(self._ogg_page.header.contents)[0]
+ self._file.write(header)
+
+ BodyBufferPtr = ctypes.POINTER(ctypes.c_ubyte * self._ogg_page.body_len)
+ body = BodyBufferPtr(self._ogg_page.body.contents)[0]
+ self._file.write(body)
+
+ def _flush(self):
+ """ Flush all pages to the file. """
+ while ogg.ogg_stream_flush(
+ ctypes.pointer(self._stream_state),
+ ctypes.pointer(self._ogg_page)) != 0:
+ self._write_page()
+
+ def _write_headers(self, custom_pre_skip):
+ """ Write the two Opus header packets."""
+ pre_skip = self._write_identification_header_packet(
+ custom_pre_skip
+ )
+ self._write_comment_header_packet()
+
+ # Store that the headers have been written
+ self._headers_written = True
+
+ # Write out pages to file to ensure that the headers are
+ # the only packets to appear on the first page. If this
+ # is not done, the file cannot be read by the library
+ # opusfile.
+ self._flush()
+
+ return pre_skip
+
+ def _write_packet(self):
+ # Place the packet into the stream
+ result = ogg.ogg_stream_packetin(
+ self._stream_state,
+ self._ogg_packet
+ )
+
+ # Check for errors
+ if result != 0:
+ raise PyOggError(
+ "Error while placing packet in Ogg stream"
+ )
+
+ # Write out pages to file
+ while ogg.ogg_stream_pageout(
+ ctypes.pointer(self._stream_state),
+ ctypes.pointer(self._ogg_page)) != 0:
+ self._write_page()
+
+ def _write_silence(self, samples):
+ """ Write a frame of silence. """
+ silence_length = (
+ samples
+ * self._encoder._channels
+ * ctypes.sizeof(opus.opus_int16)
+ )
+ silence_pcm = \
+ memoryview(bytearray(b"\x00" * silence_length))
+ self._write_to_oggopus(silence_pcm)
diff --git a/sbapp/pyogg/opus.py b/sbapp/pyogg/opus.py
new file mode 100644
index 00000000..446d0ab7
--- /dev/null
+++ b/sbapp/pyogg/opus.py
@@ -0,0 +1,1377 @@
+############################################################
+# Opus license: #
+############################################################
+"""
+Copyright 2001-2011 Xiph.Org, Skype Limited, Octasic,
+ Jean-Marc Valin, Timothy B. Terriberry,
+ CSIRO, Gregory Maxwell, Mark Borgerding,
+ Erik de Castro Lopo
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+- Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Opus is subject to the royalty-free patent licenses which are
+specified at:
+
+Xiph.Org Foundation:
+https://datatracker.ietf.org/ipr/1524/
+
+Microsoft Corporation:
+https://datatracker.ietf.org/ipr/1914/
+
+Broadcom Corporation:
+https://datatracker.ietf.org/ipr/1526/
+"""
+
+############################################################
+# Opusfile license: #
+############################################################
+"""
+Copyright (c) 1994-2013 Xiph.Org Foundation and contributors
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+- Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+- Neither the name of the Xiph.Org Foundation nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""
+
+############################################################
+# OpenSSL license: #
+############################################################
+"""
+/*
+ * Copyright (c) 1998-2019 The OpenSSL Project. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ * nor may "OpenSSL" appear in their names without prior written
+ * permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com). This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+"""
+############################################################
+# Opusenc license: #
+############################################################
+"""
+Copyright (c) 1994-2013 Xiph.Org Foundation and contributors
+Copyright (c) 2017 Jean-Marc Valin
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+- Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+- Neither the name of the Xiph.Org Foundation nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""
+
+import ctypes
+import ctypes.util
+import os
+
+from traceback import print_exc as _print_exc
+
+from .ogg import *
+
+from .library_loader import Library, ExternalLibrary, ExternalLibraryError
+
+__here = os.getcwd()
+libopus = None
+
+try:
+ names = {
+ "Windows": "opus.dll",
+ "Darwin": "libopus.0.dylib",
+ "external": "opus"
+ }
+ libopus = Library.load(names, tests = [lambda lib: hasattr(lib, "opus_encoder_get_size")])
+except ExternalLibraryError:
+ pass
+except:
+ _print_exc()
+
+libopusfile = None
+
+try:
+ names = {
+ "Windows": "opusfile.dll",
+ "Darwin": "libopusfile.0.dylib",
+ "external": "opusfile"
+ }
+ libopusfile = Library.load(names, tests = [lambda lib: hasattr(lib, "opus_head_parse")])
+except ExternalLibraryError:
+ pass
+except:
+ _print_exc()
+
+libopusenc = None
+
+try:
+ names = {
+ "Windows": "opusenc.dll",
+ "Darwin": "libopusenc.0.dylib",
+ "external": "opusenc"
+ }
+ libopusenc = Library.load(names, tests = [lambda lib: hasattr(lib, "ope_comments_create")])
+except ExternalLibraryError:
+ pass
+except:
+ _print_exc()
+
+if libopus:
+ PYOGG_OPUS_AVAIL = True
+else:
+ PYOGG_OPUS_AVAIL = False
+
+if libopusfile:
+ PYOGG_OPUS_FILE_AVAIL = True
+else:
+ PYOGG_OPUS_FILE_AVAIL = False
+
+if libopusenc:
+ PYOGG_OPUS_ENC_AVAIL = True
+else:
+ PYOGG_OPUS_ENC_AVAIL = False
+
+# Definitions of C constants
+
+# 2021-02-16: Moved definitions outside of test for PYOGG_OPUS_AVAIL
+# and PYOGG_OPUS_FILE_AVAIL as the definitons don't actually require
+# the libraries.
+
+OPE_API_VERSION =0
+
+OPE_OK =0
+OPE_BAD_ARG =-11
+OPE_INTERNAL_ERROR =-13
+OPE_UNIMPLEMENTED =-15
+OPE_ALLOC_FAIL =-17
+
+OPE_CANNOT_OPEN =-30
+OPE_TOO_LATE =-31
+OPE_UNRECOVERABLE =-32
+OPE_INVALID_PICTURE =-33
+OPE_INVALID_ICON =-34
+
+OPUS_OK =0
+OPUS_BAD_ARG =-1
+OPUS_BUFFER_TOO_SMALL =-2
+OPUS_INTERNAL_ERROR =-3
+OPUS_INVALID_PACKET =-4
+OPUS_UNIMPLEMENTED =-5
+OPUS_INVALID_STATE =-6
+OPUS_ALLOC_FAIL =-7
+
+OP_FALSE =(-1)
+OP_EOF =(-2)
+OP_HOLE =(-3)
+OP_EREAD =(-128)
+OP_EFAULT =(-129)
+OP_EIMPL =(-130)
+OP_EINVAL =(-131)
+OP_ENOTFORMAT =(-132)
+OP_EBADHEADER =(-133)
+OP_EVERSION =(-134)
+OP_ENOTAUDIO =(-135)
+OP_EBADPACKET =(-136)
+OP_EBADLINK =(-137)
+OP_ENOSEEK =(-138)
+OP_EBADTIMESTAMP =(-139)
+
+OP_PIC_FORMAT_UNKNOWN =(-1)
+OP_PIC_FORMAT_URL =(0)
+OP_PIC_FORMAT_JPEG =(1)
+OP_PIC_FORMAT_PNG =(2)
+OP_PIC_FORMAT_GIF =(3)
+
+
+OPUS_CHANNEL_COUNT_MAX =(255)
+
+OPUS_SET_APPLICATION_REQUEST =4000
+OPUS_GET_APPLICATION_REQUEST =4001
+OPUS_SET_BITRATE_REQUEST =4002
+OPUS_GET_BITRATE_REQUEST =4003
+OPUS_SET_MAX_BANDWIDTH_REQUEST =4004
+OPUS_GET_MAX_BANDWIDTH_REQUEST =4005
+OPUS_SET_VBR_REQUEST =4006
+OPUS_GET_VBR_REQUEST =4007
+OPUS_SET_BANDWIDTH_REQUEST =4008
+OPUS_GET_BANDWIDTH_REQUEST =4009
+OPUS_SET_COMPLEXITY_REQUEST =4010
+OPUS_GET_COMPLEXITY_REQUEST =4011
+OPUS_SET_INBAND_FEC_REQUEST =4012
+OPUS_GET_INBAND_FEC_REQUEST =4013
+OPUS_SET_PACKET_LOSS_PERC_REQUEST =4014
+OPUS_GET_PACKET_LOSS_PERC_REQUEST =4015
+OPUS_SET_DTX_REQUEST =4016
+OPUS_GET_DTX_REQUEST =4017
+OPUS_SET_VBR_CONSTRAINT_REQUEST =4020
+OPUS_GET_VBR_CONSTRAINT_REQUEST =4021
+OPUS_SET_FORCE_CHANNELS_REQUEST =4022
+OPUS_GET_FORCE_CHANNELS_REQUEST =4023
+OPUS_SET_SIGNAL_REQUEST =4024
+OPUS_GET_SIGNAL_REQUEST =4025
+OPUS_GET_LOOKAHEAD_REQUEST =4027
+OPUS_GET_SAMPLE_RATE_REQUEST =4029
+OPUS_GET_FINAL_RANGE_REQUEST =4031
+OPUS_GET_PITCH_REQUEST =4033
+OPUS_SET_GAIN_REQUEST =4034
+OPUS_GET_GAIN_REQUEST =4045
+OPUS_SET_LSB_DEPTH_REQUEST =4036
+OPUS_GET_LSB_DEPTH_REQUEST =4037
+OPUS_GET_LAoe_pACKET_DURATION_REQUEST =4039
+OPUS_SET_EXPERT_FRAME_DURATION_REQUEST =4040
+OPUS_GET_EXPERT_FRAME_DURATION_REQUEST =4041
+OPUS_SET_PREDICTION_DISABLED_REQUEST =4042
+OPUS_GET_PREDICTION_DISABLED_REQUEST =4043
+OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST =4046
+OPUS_GET_PHASE_INVERSION_DISABLED_REQUEST =4047
+
+OPUS_AUTO =-1000
+OPUS_BITRATE_MAX = -1
+
+OPUS_APPLICATION_VOIP = 2048
+OPUS_APPLICATION_AUDIO = 2049
+OPUS_APPLICATION_RESTRICTED_LOWDELAY =2051
+
+OPUS_SIGNAL_VOICE =3001
+OPUS_SIGNAL_MUSIC =3002
+OPUS_BANDWIDTH_NARROWBAND =1101
+OPUS_BANDWIDTH_MEDIUMBAND =1102
+OPUS_BANDWIDTH_WIDEBAND =1103
+OPUS_BANDWIDTH_SUPERWIDEBAND =1104
+OPUS_BANDWIDTH_FULLBAND =1105
+
+OPUS_FRAMESIZE_ARG =5000
+OPUS_FRAMESIZE_2_5_MS =5001
+OPUS_FRAMESIZE_5_MS =5002
+OPUS_FRAMESIZE_10_MS =5003
+OPUS_FRAMESIZE_20_MS =5004
+OPUS_FRAMESIZE_40_MS =5005
+OPUS_FRAMESIZE_60_MS =5006
+OPUS_FRAMESIZE_80_MS =5007
+OPUS_FRAMESIZE_100_MS =5008
+OPUS_FRAMESIZE_120_MS =5009
+
+OPUS_MULTISTREAM_GET_ENCODER_STATE_REQUEST =5120
+OPUS_MULTISTREAM_GET_DECODER_STATE_REQUEST =5122
+
+OP_SSL_SKIP_CERTIFICATE_CHECK_REQUEST =(6464)
+OP_HTTP_PROXY_HOST_REQUEST =(6528)
+OP_HTTP_PROXY_PORT_REQUEST =(6592)
+OP_HTTP_PROXY_USER_REQUEST =(6656)
+OP_HTTP_PROXY_PASS_REQUEST =(6720)
+OP_GET_SERVER_INFO_REQUEST =(6784)
+
+OP_DEC_FORMAT_SHORT =(7008)
+OP_DEC_FORMAT_FLOAT =(7040)
+OP_DEC_USE_DEFAULT =(6720)
+
+OP_HEADER_GAIN =(0)
+OP_ALBUM_GAIN =(3007)
+OP_TRACK_GAIN =(3008)
+OP_ABSOLUTE_GAIN =(3009)
+
+OPUS_RESET_STATE =4028
+
+OPE_SET_DECISION_DELAY_REQUEST =14000
+OPE_GET_DECISION_DELAY_REQUEST =14001
+OPE_SET_MUXING_DELAY_REQUEST =14002
+OPE_GET_MUXING_DELAY_REQUEST =14003
+OPE_SET_COMMENT_PADDING_REQUEST =14004
+OPE_GET_COMMENT_PADDING_REQUEST =14005
+OPE_SET_SERIALNO_REQUEST =14006
+OPE_GET_SERIALNO_REQUEST =14007
+OPE_SET_PACKET_CALLBACK_REQUEST =14008
+OPE_SET_HEADER_GAIN_REQUEST =14010
+OPE_GET_HEADER_GAIN_REQUEST =14011
+
+# opus_types
+
+opus_int16 = c_int16
+opus_int16_p = POINTER(c_int16)
+opus_uint16 = c_uint16
+opus_int32 = c_int32
+opus_int32_p = POINTER(opus_int32)
+opus_uint32 = c_uint32
+
+opus_int = c_int
+opus_int64= c_longlong
+opus_int8= c_int8
+
+opus_uint= c_uint
+opus_uint64 = c_ulonglong
+opus_uint8 = c_int8
+
+# /opus_types
+
+
+if PYOGG_OPUS_AVAIL:
+ # Sanity check also satisfies mypy type checking
+ assert libopus is not None
+
+ # opus
+
+ class OpusEncoder(ctypes.Structure):
+ _fields_ = [("dummy", ctypes.c_int)]
+
+ oe_p = POINTER(OpusEncoder)
+
+ libopus.opus_encoder_get_size.restype = c_int
+ libopus.opus_encoder_get_size.argtypes = [c_int]
+
+ def opus_encoder_get_size(channels):
+ return libopus.opus_encoder_get_size(channels)
+
+ libopus.opus_encoder_create.restype = oe_p
+ libopus.opus_encoder_create.argtypes = [opus_int32, c_int, c_int, c_int_p]
+
+ def opus_encoder_create(Fs, channels, application, error):
+ return libopus.opus_encoder_create(Fs, channels, application, error)
+
+ libopus.opus_encoder_init.restype = c_int
+ libopus.opus_encoder_init.argtypes = [oe_p, opus_int32, c_int, c_int]
+
+ def opus_encoder_init(st, Fs, channels, applications):
+ return libopus.opus_encoder_init(st, Fs, channels, applications)
+
+ libopus.opus_encode.restype = opus_int32
+ libopus.opus_encode.argtypes = [oe_p, opus_int16_p, c_int, c_uchar_p, opus_int32]
+
+ def opus_encode(st, pcm, frame_size, data, max_data_bytes):
+ return libopus.opus_encode(st, pcm, frame_size, data, max_data_bytes)
+
+ libopus.opus_encode_float.restype = opus_int32
+ libopus.opus_encode_float.argtypes = [oe_p, c_float_p, c_int, c_uchar_p, opus_int32]
+
+ def opus_encode_float(st, pcm, frame_size, data, max_data_bytes):
+ return libopus.opus_encode_float(st, pcm, frame_size, data, max_data_bytes)
+
+ libopus.opus_encoder_destroy.restype = None
+ libopus.opus_encoder_destroy.argtypes = [oe_p]
+
+ def opus_encoder_destroy(st):
+ return libopus.opus_encoder_destroy(st)
+
+ libopus.opus_encoder_ctl.restype = c_int
+ libopus.opus_encoder_ctl.argtypes = [oe_p, c_int]
+
+ def opus_encoder_ctl(st, request, *args):
+ return libopus.opus_encoder_ctl(st, request, *args)
+
+ class OpusDecoder(ctypes.Structure):
+ _fields_ = [("dummy", c_int)]
+
+ od_p = POINTER(OpusDecoder)
+
+ libopus.opus_decoder_get_size.restype = c_int
+ libopus.opus_decoder_get_size.argtypes = [c_int]
+
+ def opus_decoder_get_size(channels):
+ return libopus.opus_decoder_get_size(channels)
+
+ libopus.opus_decoder_create.restype = od_p
+ libopus.opus_decoder_create.argtypes = [opus_int32, c_int, c_int_p]
+
+ def opus_decoder_create(Fs, channels, error):
+ return libopus.opus_decoder_create(Fs, channels, error)
+
+ libopus.opus_decoder_init.restype = c_int
+ libopus.opus_decoder_init.argtypes = [od_p, opus_int32, c_int]
+
+ def opus_decoder_init(st, Fs, channels):
+ return libopus.opus_decoder_init(st, Fs, channels)
+
+ libopus.opus_decode.restype = c_int
+ libopus.opus_decode.argtypes = [od_p, c_uchar_p, opus_int32, opus_int16_p, c_int, c_int]
+
+ def opus_decode(st, data, len, pcm, frame_size, decode_fec):
+ return libopus.opus_decode(st, data, len, pcm, frame_size, decode_fec)
+
+ libopus.opus_decode_float.restype = c_int
+ libopus.opus_decode_float.argtypes = [od_p, c_uchar_p, opus_int32, c_float_p, c_int, c_int]
+
+ def opus_decode_float(st, data, len, pcm, frame_size, decode_fec):
+ return libopus.opus_decode_float(st, data, len, pcm, frame_size, decode_fec)
+
+ libopus.opus_decoder_ctl.restype = c_int
+ libopus.opus_decoder_ctl.argtypes = [od_p, c_int]
+
+ def opus_decoder_ctl(st, request, *args):
+ return libopus.opus_decoder_ctl(st, request, *args)
+
+ libopus.opus_decoder_destroy.restype = None
+ libopus.opus_decoder_destroy.argtypes = [od_p]
+
+ def opus_decoder_destroy(st):
+ return libopus.opus_decoder_destroy(st)
+
+ libopus.opus_packet_parse.restype = c_int
+ libopus.opus_packet_parse.argtypes = [c_uchar_p, opus_int32, c_uchar_p, c_uchar_p*48, opus_int16*48, c_int_p]
+
+ def opus_packet_parse(data, len, out_toc, frames, size, payload_offset):
+ return libopus.opus_packet_parse(data, len, out_toc, frames, size, payload_offset)
+
+ libopus.opus_packet_get_bandwidth.restype = c_int
+ libopus.opus_packet_get_bandwidth.argtypes = [c_uchar_p]
+
+ def opus_packet_get_bandwidth(data):
+ return libopus.opus_packet_get_bandwidth(data)
+
+ libopus.opus_packet_get_samples_per_frame.restype = c_int
+ libopus.opus_packet_get_samples_per_frame.argtypes = [c_uchar_p, opus_int32]
+
+ def opus_packet_get_samples_per_frame(data, Fs):
+ return libopus.opus_packet_get_samples_per_frame(data, Fs)
+
+ libopus.opus_packet_get_nb_channels.restype = c_int
+ libopus.opus_packet_get_nb_channels.argtypes = [c_uchar_p]
+
+ def opus_packet_get_nb_channels(data):
+ return libopus.opus_packet_get_nb_channels(data)
+
+ libopus.opus_packet_get_nb_frames.restype = c_int
+ libopus.opus_packet_get_nb_frames.argtypes = [c_uchar*0, opus_int32]
+
+ def opus_packet_get_nb_frames(packet, len):
+ return libopus.opus_packet_get_nb_frames(packet, len)
+
+ libopus.opus_packet_get_nb_samples.restype = c_int
+ libopus.opus_packet_get_nb_samples.argtypes = [c_uchar*0, opus_int32, opus_int32]
+
+ def opus_packet_get_nb_samples(packet, len, Fs):
+ return libopus.opus_packet_get_nb_samples(packet, len, Fs)
+
+ libopus.opus_decoder_get_nb_samples.restype = c_int
+ libopus.opus_decoder_get_nb_samples.argtypes = [od_p, c_uchar*0, opus_int32]
+
+ def opus_decoder_get_nb_samples(dec, packet, len):
+ return libopus.opus_decoder_get_nb_samples(dec, packet, len)
+
+ libopus.opus_pcm_soft_clip.restype = None
+ libopus.opus_pcm_soft_clip.argtypes = [c_float_p, c_int, c_int, c_float_p]
+
+ def opus_pcm_soft_clip(pcm, frame_size, channels, softclip_mem):
+ return libopus.opus_pcm_soft_clip(pcm, frame_size, channels, softclip_mem)
+
+ class OpusRepacketizer(ctypes.Structure):
+ _fields_ = [("dummy", c_int)]
+
+ or_p = POINTER(OpusRepacketizer)
+
+ libopus.opus_repacketizer_get_size.restype = c_int
+ libopus.opus_repacketizer_get_size.argtypes = []
+
+ def opus_repacketizer_get_size():
+ return libopus.opus_repacketizer_get_size()
+
+ libopus.opus_repacketizer_init.restype = or_p
+ libopus.opus_repacketizer_init.argtypes = [or_p]
+
+ def opus_repacketizer_init(rp):
+ return libopus.opus_repacketizer_init(rp)
+
+ libopus.opus_repacketizer_create.restype = or_p
+ libopus.opus_repacketizer_create.argtypes = []
+
+ def opus_repacketizer_create():
+ return libopus.opus_repacketizer_create()
+
+ libopus.opus_repacketizer_destroy.restype = None
+ libopus.opus_repacketizer_destroy.argtypes = [or_p]
+
+ def opus_repacketizer_destroy(rp):
+ return libopus.opus_repacketizer_destroy(rp)
+
+ libopus.opus_repacketizer_cat.restype = c_int
+ libopus.opus_repacketizer_cat.argtypes = [or_p, c_uchar_p, opus_int32]
+
+ def opus_repacketizer_cat(rp, data, len):
+ return libopus.opus_repacketizer_cat(rp, data, len)
+
+ libopus.opus_repacketizer_out_range.restype = opus_int32
+ libopus.opus_repacketizer_out_range.argtypes = [or_p, c_int, c_int, c_uchar_p, opus_int32]
+
+ def opus_repacketizer_out_range(rp, begin, end, data, maxlen):
+ return libopus.opus_repacketizer_out_range(rp, begin, end, data, maxlen)
+
+ libopus.opus_repacketizer_get_nb_frames.restype = c_int
+ libopus.opus_repacketizer_get_nb_frames.argtypes = [or_p]
+
+ def opus_repacketizer_get_nb_frames(rp):
+ return libopus.opus_repacketizer_get_nb_frames(rp)
+
+ libopus.opus_repacketizer_out.restype = opus_int32
+ libopus.opus_repacketizer_out.argtypes = [or_p, c_uchar_p, opus_int32]
+
+ def opus_repacketizer_out(rp, data, maxlen):
+ return libopus.opus_repacketizer_out(rp, data, maxlen)
+
+ libopus.opus_packet_pad.restype = c_int
+ libopus.opus_packet_pad.argtypes = [c_uchar_p, opus_int32, opus_int32]
+
+ def opus_packet_pad(data, len, new_len):
+ return libopus.opus_packet_pad(data, len, new_len)
+
+ libopus.opus_packet_unpad.restype = opus_int32
+ libopus.opus_packet_unpad.argtypes = [c_uchar_p, opus_int32]
+
+ def opus_packet_unpad(data, len):
+ return libopus.opus_packet_unpad(data, len)
+
+ libopus.opus_multistream_packet_pad.restype = c_int
+ libopus.opus_multistream_packet_pad.argtypes = [c_uchar_p, opus_int32, opus_int32, c_int]
+
+ def opus_multistream_packet_pad(data, len, new_len, nb_streams):
+ return libopus.opus_multistream_packet_pad(data, len, new_len, nb_streams)
+
+ libopus.opus_multistream_packet_unpad.restype = opus_int32
+ libopus.opus_multistream_packet_unpad.argtypes = [c_uchar_p, opus_int32, c_int]
+
+ def opus_multistream_packet_unpad(data, len, nb_streams):
+ return libopus.opus_multistream_packet_unpad(data, len, nb_streams)
+
+ libopus.opus_strerror.restype = c_char_p
+ libopus.opus_strerror.argtypes = [c_int]
+
+ def opus_strerror(error):
+ return libopus.opus_strerror(error)
+
+ libopus.opus_get_version_string.restype = c_char_p
+ libopus.opus_get_version_string.argtypes = []
+
+ def opus_get_version_string():
+ return libopus.opus_get_version_string()
+
+ # /opus
+
+ # opus_multistream
+ class OpusMSEncoder(ctypes.Structure):
+ _fields_ = [("dummy", c_int)]
+ omse_p = POINTER(OpusMSEncoder)
+
+ class OpusMSDecoder(ctypes.Structure):
+ _fields_ = [("dummy", c_int)]
+ omsd_p = POINTER(OpusMSDecoder)
+
+ libopus.opus_multistream_encoder_get_size.restype = opus_int32
+ libopus.opus_multistream_encoder_get_size.argtypes = [c_int, c_int]
+
+ def opus_multistream_encoder_get_size(streams, coupled_streams):
+ return libopus.opus_multistream_encoder_get_size(streams, coupled_streams)
+
+ libopus.opus_multistream_surround_encoder_get_size.restype = opus_int32
+ libopus.opus_multistream_surround_encoder_get_size.argtypes = [c_int, c_int]
+
+ def opus_multistream_surround_encoder_get_size(channels, mapping_family):
+ return libopus.opus_multistream_surround_encoder_get_size(channels, mapping_family)
+
+ libopus.opus_multistream_encoder_create.restype = omse_p
+ libopus.opus_multistream_encoder_create.argtypes = [opus_int32, c_int, c_int, c_int, c_uchar_p, c_int, c_int_p]
+
+ def opus_multistream_encoder_create(Fs, channels,streams,coupled_streams, mapping, application, error):
+ return libopus.opus_multistream_encoder_create(Fs, channels,streams,coupled_streams, mapping, application, error)
+
+ libopus.opus_multistream_surround_encoder_create.restype = omse_p
+ libopus.opus_multistream_surround_encoder_create.argtypes = [opus_int32, c_int, c_int, c_int_p, c_int_p, c_uchar_p, c_int, c_int_p]
+
+ def opus_multistream_surround_encoder_create(Fs, channels, mapping_family, streams, coupled_streams, mapping, application, error):
+ return libopus.opus_multistream_surround_encoder_create(Fs, channels, mapping_family, streams, coupled_streams, mapping, application, error)
+
+ libopus.opus_multistream_encoder_init.restype = c_int
+ libopus.opus_multistream_encoder_init.argtypes = [omse_p, opus_int32, c_int, c_int, c_int, c_uchar_p, c_int]
+
+ def opus_multistream_encoder_init(st, Fs, channels, streams, coupled_streams, mapping, application):
+ return libopus.opus_multistream_encoder_init(st, Fs, channels, streams, coupled_streams, mapping, application)
+
+ libopus.opus_multistream_surround_encoder_init.restype = c_int
+ libopus.opus_multistream_surround_encoder_init.argtypes = [omse_p, opus_int32, c_int, c_int, c_int_p, c_int_p, c_uchar_p, c_int]
+
+ def opus_multistream_surround_encoder_init(st, Fs, channels, mapping_family, streams, coupled_streams, mapping, application):
+ return libopus.opus_multistream_surround_encoder_init(st, Fs, channels, mapping_family, streams, coupled_streams, mapping, application)
+
+ libopus.opus_multistream_encode.restype = c_int
+ libopus.opus_multistream_encode.argtypes = [omse_p, opus_int16_p, c_int, c_uchar_p, opus_int32]
+
+ def opus_multistream_encode(st, pcm, frame_size, data, max_data_bytes):
+ return libopus.opus_multistream_encode(st, pcm, frame_size, data, max_data_bytes)
+
+ libopus.opus_multistream_encode_float.restype = c_int
+ libopus.opus_multistream_encode_float.argtypes = [omse_p, c_float_p, c_int, c_uchar_p, opus_int32]
+
+ def opus_multistream_encode_float(st, pcm, frame_size, data, max_data_bytes):
+ return libopus.opus_multistream_encode_float(st, pcm, frame_size, data, max_data_bytes)
+
+ libopus.opus_multistream_encoder_destroy.restype = None
+ libopus.opus_multistream_encoder_destroy.argtypes = [omse_p]
+
+ def opus_multistream_encoder_destroy(st):
+ return libopus.opus_multistream_encoder_destroy(st)
+
+ libopus.opus_multistream_encoder_ctl.restype = c_int
+ libopus.opus_multistream_encoder_ctl.argtypes = [omse_p, c_int]
+
+ def opus_multistream_encoder_ctl(st, request, *args):
+ return libopus.opus_multistream_encoder_ctl(st, request, *args)
+
+ libopus.opus_multistream_decoder_get_size.restype = opus_int32
+ libopus.opus_multistream_decoder_get_size.argtypes = [c_int, c_int]
+
+ def opus_multistream_decoder_get_size(streams, coupled_streams):
+ return libopus.opus_multistream_decoder_get_size(streams, coupled_streams)
+
+ libopus.opus_multistream_decoder_create.restype = omsd_p
+ libopus.opus_multistream_decoder_create.argtypes = [opus_int32, c_int, c_int, c_int, c_uchar_p, c_int_p]
+
+ def opus_multistream_decoder_create(Fs, channels, streams, coupled_streams, mapping, error):
+ return libopus.opus_multistream_decoder_create(Fs, channels, streams, coupled_streams, mapping, error)
+
+ libopus.opus_multistream_decoder_init.restype = c_int
+ libopus.opus_multistream_decoder_init.argtypes = [omsd_p, opus_int32, c_int, c_int, c_int, c_uchar_p]
+
+ def opus_multistream_decoder_init(st, Fs, channels, streams, coupled_streams, mapping):
+ return libopus.opus_multistream_decoder_init(st, Fs, channels, streams, coupled_streams, mapping)
+
+ libopus.opus_multistream_decode.restype = c_int
+ libopus.opus_multistream_decode.argtypes = [omsd_p, c_uchar_p, opus_int32, opus_int16_p, c_int, c_int]
+
+ def opus_multistream_decode(st, data, len, pcm, frame_size, decode_fec):
+ return libopus.opus_multistream_decode(st, data, len, pcm, frame_size, decode_fec)
+
+ libopus.opus_multistream_decode_float.restype = c_int
+ libopus.opus_multistream_decode_float.argtypes = [omsd_p, c_uchar_p, opus_int32, c_float_p, c_int, c_int]
+
+ def opus_multistream_decode_float(st, data, len, pcm, frame_size, decode_fec):
+ return libopus.opus_multistream_decode_float(st, data, len, pcm, frame_size, decode_fec)
+
+ libopus.opus_multistream_decoder_ctl.restype = c_int
+ libopus.opus_multistream_decoder_ctl.argtypes = [omsd_p, c_int]
+
+ def opus_multistream_decoder_ctl(st, request, *args):
+ return libopus.opus_multistream_decoder_ctl(st, request, *args)
+
+ libopus.opus_multistream_decoder_destroy.restype = None
+ libopus.opus_multistream_decoder_destroy.argtypes = [omsd_p]
+
+ def opus_multistream_decoder_destroy(st):
+ return libopus.opus_multistream_decoder_destroy(st)
+
+ # /opus_multistream
+
+ if PYOGG_OPUS_FILE_AVAIL:
+ assert libopusfile is not None
+
+ # opusfile
+
+ class OggOpusFile(ctypes.Structure):
+ _fields_ = [("dummy", c_int)]
+
+ oof_p = POINTER(OggOpusFile)
+
+
+
+ class OpusHead(ctypes.Structure):
+ _fields_ = [("version", c_int),
+ ("channel_count", c_int),
+ ("pre_skip", c_uint),
+ ("input_sample_rate", opus_uint32),
+ ("output_gain", c_int),
+ ("mapping_family", c_int),
+ ("stream_count", c_int),
+ ("coupled_count", c_int),
+ ("mapping", c_uchar * OPUS_CHANNEL_COUNT_MAX)]
+
+ oh_p = POINTER(OpusHead)
+
+ class OpusTags(ctypes.Structure):
+ _fields_ = [("user_comments", c_char_p_p),
+ ("comment_lengths", c_int_p),
+ ("comments", c_int),
+ ("vendor", c_char_p)]
+
+ ot_p = POINTER(OpusTags)
+
+
+
+ class OpusPictureTag(ctypes.Structure):
+ _fields_ = [("type", opus_int32),
+ ("mime_type", c_char_p),
+ ("description", c_char_p),
+ ("width", opus_uint32),
+ ("height", opus_uint32),
+ ("depth", opus_uint32),
+ ("colors", opus_uint32),
+ ("data_length", opus_uint32),
+ ("data", c_uchar_p),
+ ("format", c_int)]
+
+ opt_p = POINTER(OpusPictureTag)
+
+ libopusfile.opus_head_parse.restype = c_int
+ libopusfile.opus_head_parse.argtypes = [oh_p, c_uchar_p, c_size_t]
+
+ def opus_head_parse(_head, _data, _len):
+ return libopusfile.opus_head_parse(_head, _data, _len)
+
+ libopusfile.opus_granule_sample.restype = ogg_int64_t
+ libopusfile.opus_granule_sample.argtypes = [oh_p, ogg_int64_t]
+
+ def opus_granule_sample(_head, _gp):
+ return libopusfile.opus_granule_sample(_head, _gp)
+
+ libopusfile.opus_tags_parse.restype = c_int
+ libopusfile.opus_tags_parse.argtypes = [ot_p, c_uchar_p, c_size_t]
+
+ def opus_tags_parse(_tags, _data, _len):
+ return libopusfile.opus_tags_parse(_tags, _data, _len)
+
+ libopusfile.opus_tags_copy.restype = c_int
+ libopusfile.opus_tags_copy.argtypes = [ot_p, ot_p]
+
+ def opus_tags_copy(_dst, _src):
+ return libopusfile.opus_tags_copy(_dst, _src)
+
+ libopusfile.opus_tags_init.restype = None
+ libopusfile.opus_tags_init.argtypes = [ot_p]
+
+ def opus_tags_init(_tags):
+ return libopusfile.opus_tags_init(_tags)
+
+ libopusfile.opus_tags_add.restype = c_int
+ libopusfile.opus_tags_add.argtypes = [ot_p, c_char_p, c_char_p]
+
+ def opus_tags_add(_tags, _tag, _value):
+ return libopusfile.opus_tags_add(_tags, _tag, _value)
+
+ libopusfile.opus_tags_add_comment.restype = c_int
+ libopusfile.opus_tags_add_comment.argtypes = [ot_p, c_char_p]
+
+ def opus_tags_add_comment(_tags, _comment):
+ return libopusfile.opus_tags_add_comment(_tags, _comment)
+
+ libopusfile.opus_tags_set_binary_suffix.restype = c_int
+ libopusfile.opus_tags_set_binary_suffix.argtypes = [ot_p, c_uchar_p, c_int]
+
+ def opus_tags_set_binary_suffix(_tags, _data, _len):
+ return libopusfile.opus_tags_set_binary_suffix(_tags, _data, _len)
+
+ libopusfile.opus_tags_query.restype = c_char_p
+ libopusfile.opus_tags_query.argtypes = [ot_p, c_char_p, c_int]
+
+ def opus_tags_query(_tags, _tag, _count):
+ return libopusfile.opus_tags_query(_tags, _tag, _count)
+
+ libopusfile.opus_tags_query_count.restype = c_int
+ libopusfile.opus_tags_query_count.argtypes = [ot_p, c_char_p]
+
+ def opus_tags_query_count(_tags, _tag):
+ return libopusfile.opus_tags_query_count(_tags, _tag)
+
+ libopusfile.opus_tags_get_binary_suffix.restype = c_uchar_p
+ libopusfile.opus_tags_get_binary_suffix.argtypes = [ot_p, c_int_p]
+
+ def opus_tags_get_binary_suffix(_tags, _len):
+ return libopusfile.opus_tags_get_binary_suffix(_tags, _len)
+
+ libopusfile.opus_tags_get_album_gain.restype = c_int
+ libopusfile.opus_tags_get_album_gain.argtypes = [ot_p, c_int_p]
+
+ def opus_tags_get_album_gain(_tags, _gain_q8):
+ return libopusfile.opus_tags_get_album_gain(_tags, _gain_q8)
+
+ libopusfile.opus_tags_get_track_gain.restype = c_int
+ libopusfile.opus_tags_get_track_gain.argtypes = [ot_p, c_int_p]
+
+ def opus_tags_get_track_gain(_tags, _gain_q8):
+ return libopusfile.opus_tags_get_track_gain(_tags, _gain_q8)
+
+ libopusfile.opus_tags_clear.restype = None
+ libopusfile.opus_tags_clear.argtypes = [ot_p]
+
+ def opus_tags_clear(_tags):
+ return libopusfile.opus_tags_clear(_tags)
+
+ libopusfile.opus_tagcompare.restype = c_int
+ libopusfile.opus_tagcompare.argtypes = [c_char_p, c_char_p]
+
+ def opus_tagcompare(_tag_name, _comment):
+ return libopusfile.opus_tagcompare(_tag_name, _comment)
+
+ libopusfile.opus_tagncompare.restype = c_int
+ libopusfile.opus_tagncompare.argtypes = [c_char_p, c_int, c_char_p]
+
+ def opus_tagncompare(_tag_name, _tag_len, _comment):
+ return libopusfile.opus_tagncompare(_tag_name, _tag_len, _comment)
+
+ libopusfile.opus_picture_tag_parse.restype = c_int
+ libopusfile.opus_picture_tag_parse.argtypes = [opt_p, c_char_p]
+
+ def opus_picture_tag_parse(_pic, _tag):
+ return libopusfile.opus_picture_tag_parse(_pic, _tag)
+
+ libopusfile.opus_picture_tag_init.restype = None
+ libopusfile.opus_picture_tag_init.argtypes = [opt_p]
+
+ def opus_picture_tag_init(_pic):
+ return libopusfile.opus_picture_tag_init(_pic)
+
+ libopusfile.opus_picture_tag_clear.restype = None
+ libopusfile.opus_picture_tag_clear.argtypes = [opt_p]
+
+ def opus_picture_tag_clear(_pic):
+ return libopusfile.opus_picture_tag_clear(_pic)
+
+
+
+ class OpusServerInfo(ctypes.Structure):
+ _fields_ = [("name", c_char_p),
+ ("description", c_char_p),
+ ("genre", c_char_p),
+ ("url", c_char_p),
+ ("server", c_char_p),
+ ("content_type", c_char_p),
+ ("bitrate_kbps", opus_int32),
+ ("is_public", c_int),
+ ("is_ssl", c_int)]
+
+ osi_p = POINTER(OpusServerInfo)
+ try:
+ libopusfile.opus_server_info_init.restype = None
+ libopusfile.opus_server_info_init.argtypes = [osi_p]
+
+ def opus_server_info_init(_info):
+ return libopusfile.opus_server_info_init(_info)
+
+ libopusfile.opus_server_info_clear.restype = None
+ libopusfile.opus_server_info_clear.argtypes = [osi_p]
+
+ def opus_server_info_clear(_info):
+ return libopusfile.opus_server_info_clear(_info)
+ except:
+ pass
+
+ op_read_func = ctypes.CFUNCTYPE(c_int,
+ c_void_p,
+ c_uchar_p,
+ c_int)
+
+ op_seek_func = ctypes.CFUNCTYPE(c_int,
+ c_void_p,
+ opus_int64,
+ c_int)
+
+ op_tell_func = ctypes.CFUNCTYPE(opus_int64,
+ c_void_p)
+
+ op_close_func = ctypes.CFUNCTYPE(c_int,
+ c_void_p)
+
+ class OpusFileCallbacks(ctypes.Structure):
+ _fields_ = [("read", op_read_func),
+ ("seek", op_seek_func),
+ ("tell", op_tell_func),
+ ("close", op_close_func)]
+
+ ofc_p = POINTER(OpusFileCallbacks)
+
+ libopusfile.op_fopen.restype = c_void_p
+ libopusfile.op_fopen.argtypes = [ofc_p, c_char_p, c_char_p]
+
+ def op_fopen(_cb, _path, _mode):
+ return libopusfile.op_fopen(_cb, _path, _mode)
+
+ libopusfile.op_fdopen.restype = c_void_p
+ libopusfile.op_fdopen.argtypes = [ofc_p, c_int, c_char_p]
+
+ def op_fdopen(_cb, _fd, _mode):
+ return libopusfile.op_fdopen(_cb, _fd, _mode)
+
+ libopusfile.op_freopen.restype = c_void_p
+ libopusfile.op_freopen.argtypes = [ofc_p, c_char_p, c_char_p, c_void_p]
+
+ def op_freopen(_cb, _path, _mode, _stream):
+ return libopusfile.op_freopen(_cb, _path, _mode, _stream)
+
+ libopusfile.op_mem_stream_create.restype = c_void_p
+ libopusfile.op_mem_stream_create.argtypes = [ofc_p, c_uchar_p, c_size_t]
+
+ def op_mem_stream_create(_cb, _data, _size):
+ return libopusfile.op_mem_stream_create(_cb, _data, _size)
+
+ libopusfile.op_test.restype = c_int
+ libopusfile.op_test.argtypes = [oh_p, c_uchar_p, c_size_t]
+
+ def op_test(_head, _initial_data, _initial_bytes):
+ return libopusfile.op_test(_head, _initial_data, _initial_bytes)
+
+ libopusfile.op_open_file.restype = oof_p
+ libopusfile.op_open_file.argtypes = [c_char_p, c_int_p]
+
+ def op_open_file(_path, _error):
+ return libopusfile.op_open_file(_path, _error)
+
+ libopusfile.op_open_memory.restype = oof_p
+ libopusfile.op_open_memory.argtypes = [c_uchar_p, c_size_t, c_int_p]
+
+ def op_open_memory(_data, _size, _error):
+ return libopusfile.op_open_memory(_data, _size, _error)
+
+ libopusfile.op_open_callbacks.restype = oof_p
+ libopusfile.op_open_callbacks.argtypes = [c_void_p, ofc_p, c_uchar_p, c_size_t, c_int_p]
+
+ def op_open_callbacks(_source, _cb, _initial_data, _initial_bytes, _error):
+ return libopusfile.op_open_callbacks(_source, _cb, _initial_data, _initial_bytes, _error)
+
+ libopusfile.op_test_file.restype = oof_p
+ libopusfile.op_test_file.argtypes = [c_char_p, c_int_p]
+
+ def op_test_file(_path, _error):
+ return libopusfile.op_test_file(_path, _error)
+
+ libopusfile.op_test_memory.restype = oof_p
+ libopusfile.op_test_memory.argtypes = [c_uchar_p, c_size_t, c_int_p]
+
+ def op_test_memory(_data, _size, _error):
+ return libopusfile.op_test_memory(_data, _size, _error)
+
+ libopusfile.op_test_callbacks.restype = oof_p
+ libopusfile.op_test_callbacks.argtypes = [c_void_p, ofc_p, c_uchar_p, c_size_t, c_int_p]
+
+ def op_test_callbacks(_source, _cb, _initial_data, _initial_bytes, _error):
+ return libopusfile.op_test_callbacks(_source, _cb, _initial_data, _initial_bytes, _error)
+
+ libopusfile.op_test_open.restype = c_int
+ libopusfile.op_test_open.argtypes = [oof_p]
+
+ def op_test_open(_of):
+ return libopusfile.op_test_open(_of)
+
+ libopusfile.op_free.restype = None
+ libopusfile.op_free.argtypes = [oof_p]
+
+ def op_free(_of):
+ return libopusfile.op_free(_of)
+
+ libopusfile.op_seekable.restype = c_int
+ libopusfile.op_seekable.argtypes = [oof_p]
+
+ def op_seekable(_of):
+ return libopusfile.op_seekable(_of)
+
+ libopusfile.op_link_count.restype = c_int
+ libopusfile.op_link_count.argtypes = [oof_p]
+
+ def op_link_count(_of):
+ return libopusfile.op_link_count(_of)
+
+ libopusfile.op_serialno.restype = opus_uint32
+ libopusfile.op_serialno.argtypes = [oof_p, c_int]
+
+ def op_serialno(_of, _li):
+ return libopusfile.op_serialno(_of, _li)
+
+ libopusfile.op_channel_count.restype = c_int
+ libopusfile.op_channel_count.argtypes = [oof_p, c_int]
+
+ def op_channel_count(_of, _li):
+ return libopusfile.op_channel_count(_of, _li)
+
+ libopusfile.op_raw_total.restype = opus_int64
+ libopusfile.op_raw_total.argtypes = [oof_p, c_int]
+
+ def op_raw_total(_of, _li):
+ return libopusfile.op_raw_total(_of, _li)
+
+ libopusfile.op_pcm_total.restype = ogg_int64_t
+ libopusfile.op_pcm_total.argtypes = [oof_p, c_int]
+
+ def op_pcm_total(_of, _li):
+ return libopusfile.op_pcm_total(_of, _li)
+
+ libopusfile.op_head.restype = oh_p
+ libopusfile.op_head.argtypes = [oof_p, c_int]
+
+ def op_head(_of, _li):
+ return libopusfile.op_head(_of, _li)
+
+ libopusfile.op_tags.restype = ot_p
+ libopusfile.op_tags.argtypes = [oof_p, c_int]
+
+ def op_tags(_of, _li):
+ return libopusfile.op_tags(_of, _li)
+
+ libopusfile.op_current_link.restype = c_int
+ libopusfile.op_current_link.argtypes = [oof_p]
+
+ def op_current_link(_of):
+ return libopusfile.op_current_link(_of)
+
+ libopusfile.op_bitrate.restype = opus_int32
+ libopusfile.op_bitrate.argtypes = [oof_p, c_int]
+
+ def op_bitrate(_of, _li):
+ return libopusfile.op_bitrate(_of, _li)
+
+ libopusfile.op_bitrate_instant.restype = opus_int32
+ libopusfile.op_bitrate_instant.argtypes = [oof_p]
+
+ def op_bitrate_instant(_of):
+ return libopusfile.op_bitrate_instant(_of)
+
+ libopusfile.op_raw_tell.restype = opus_int64
+ libopusfile.op_raw_tell.argtypes = [oof_p]
+
+ def op_raw_tell(_of):
+ return libopusfile.op_raw_tell(_of)
+
+ libopusfile.op_pcm_tell.restype = ogg_int64_t
+ libopusfile.op_pcm_tell.argtypes = [oof_p]
+
+ def op_pcm_tell(_of):
+ return libopusfile.op_pcm_tell(_of)
+
+ libopusfile.op_raw_seek.restype = c_int
+ libopusfile.op_raw_seek.argtypes = [oof_p, opus_int64]
+
+ def op_raw_seek(_of, _byte_offset):
+ return libopusfile.op_raw_seek(_of, _byte_offset)
+
+ libopusfile.op_pcm_seek.restype = c_int
+ libopusfile.op_pcm_seek.argtypes = [oof_p,ogg_int64_t]
+
+ def op_pcm_seek(_of, _pcm_offset):
+ return libopusfile.op_pcm_seek(_of, _pcm_offset)
+
+
+
+ op_decode_cb_func = ctypes.CFUNCTYPE(c_int,
+ c_void_p,
+ omsd_p,
+ c_void_p,
+ op_p,
+ c_int,
+ c_int,
+ c_int,
+ c_int)
+
+ libopusfile.op_set_decode_callback.restype = None
+ libopusfile.op_set_decode_callback.argtypes = [oof_p, op_decode_cb_func, c_void_p]
+
+ def op_set_decode_callback(_of, _decode_cb, _ctx):
+ return libopusfile.op_set_decode_callback(_of, _decode_cb, _ctx)
+
+
+
+ libopusfile.op_set_gain_offset.restype = c_int
+ libopusfile.op_set_gain_offset.argtypes = [oof_p, c_int, opus_int32]
+
+ def op_set_gain_offset(_of, _gain_type, _gain_offset_q8):
+ return libopusfile.op_set_gain_offset(_of, _gain_type, _gain_offset_q8)
+
+ libopusfile.op_set_dither_enabled.restype = None
+ libopusfile.op_set_dither_enabled.argtypes = [oof_p, c_int]
+
+ def op_set_dither_enabled(_of, _enabled):
+ return libopusfile.op_set_dither_enabled(_of, _enabled)
+
+ libopusfile.op_read.restype = c_int
+ libopusfile.op_read.argtypes = [oof_p, opus_int16_p, c_int, c_int_p]
+
+ def op_read(_of, _pcm, _buf_size, _li):
+ return libopusfile.op_read(_of, _pcm, _buf_size, _li)
+
+ libopusfile.op_read_float.restype = c_int
+ libopusfile.op_read_float.argtypes = [oof_p, c_float_p, c_int, c_int_p]
+
+ def op_read_float(_of, _pcm, _buf_size, _li):
+ return libopusfile.op_read_float(_of, _pcm, _buf_size, _li)
+
+ libopusfile.op_read_stereo.restype = c_int
+ libopusfile.op_read_stereo.argtypes = [oof_p, opus_int16_p, c_int]
+
+ def op_read_stereo(_of, _pcm, _buf_size):
+ return libopusfile.op_read_stereo(_of, _pcm, _buf_size)
+
+ libopusfile.op_read_float_stereo.restype = c_int
+ libopusfile.op_read_float_stereo.argtypes = [oof_p, c_float_p, c_int]
+
+ def op_read_float_stereo(_of, _pcm, _buf_size):
+ return libopusfile.op_read_float_stereo(_of, _pcm, _buf_size)
+
+
+ if PYOGG_OPUS_ENC_AVAIL:
+ # Sanity check also satisfies mypy type checking
+ assert libopusenc is not None
+
+ ope_write_func = ctypes.CFUNCTYPE(c_int,
+ c_void_p,
+ c_uchar_p,
+ opus_int32)
+
+ ope_close_func = ctypes.CFUNCTYPE(c_int,
+ c_void_p)
+
+ ope_packet_func = ctypes.CFUNCTYPE(c_int,
+ c_void_p,
+ c_uchar_p,
+ opus_int32,
+ opus_uint32)
+
+
+ class OpusEncCallbacks(ctypes.Structure):
+ _fields_ = [("write", ope_write_func),
+ ("close", ope_close_func)]
+
+ oec_p = POINTER(OpusEncCallbacks)
+
+ class OggOpusComments(ctypes.Structure):
+ _fields_ = [("dummy", c_int)]
+
+ ooc_p = POINTER(OggOpusComments)
+
+ class OggOpusEnc(ctypes.Structure):
+ _fields_ = [("dummy", c_int)]
+
+ ooe_p = POINTER(OggOpusEnc)
+
+ libopusenc.ope_comments_create.restype = ooc_p
+ libopusenc.ope_comments_create.argtypes = []
+
+ def ope_comments_create():
+ return libopusenc.ope_comments_create()
+
+ libopusenc.ope_comments_copy.restype = ooc_p
+ libopusenc.ope_comments_copy.argtypes = [ooc_p]
+
+ def ope_comments_copy(comments):
+ return libopusenc.ope_comments_copy(comments)
+
+ libopusenc.ope_comments_destroy.restype = None
+ libopusenc.ope_comments_destroy.argtypes = [ooc_p]
+
+ def ope_comments_destroy(comments):
+ return libopusenc.ope_comments_destroy(comments)
+
+ libopusenc.ope_comments_add.restype = c_int
+ libopusenc.ope_comments_add.argtypes = [ooc_p, c_char_p, c_char_p]
+
+ def ope_comments_add(comments, tag, val):
+ return libopusenc.ope_comments_add(comments, tag, val)
+
+ libopusenc.ope_comments_add_string.restype = c_int
+ libopusenc.ope_comments_add_string.argtypes = [ooc_p, c_char_p]
+
+ def ope_comments_add_string(comments, tag_and_val):
+ return libopusenc.ope_comments_add_string(comments, tag_and_val)
+
+ libopusenc.ope_comments_add_picture.restype = c_int
+ libopusenc.ope_comments_add_picture.argtypes = [ooc_p, c_char_p, c_int, c_char_p]
+
+ def ope_comments_add_picture(comments, filename, picture_type, description):
+ return libopusenc.ope_comments_add_picture(comments, filename, picture_type, description)
+
+ libopusenc.ope_encoder_create_file.restype = ooe_p
+ libopusenc.ope_encoder_create_file.argtypes = [c_char_p, ooc_p, opus_int32, c_int, c_int, c_int_p]
+
+ def ope_encoder_create_file(path, comments, rate, channels, family, error):
+ return libopusenc.ope_encoder_create_file(path, comments, rate, channels, family, error)
+
+ libopusenc.ope_encoder_create_callbacks.restype = ooe_p
+ libopusenc.ope_encoder_create_callbacks.argtypes = [oec_p, c_void_p, ooc_p, opus_int32, c_int, c_int, c_int_p]
+
+ def ope_encoder_create_callbacks(callbacks, user_data, comments, rate, channels, family, error):
+ return libopusenc.ope_encoder_create_callbacks(callbacks, user_data, comments, rate, channels, family, error)
+
+ libopusenc.ope_encoder_create_pull.restype = ooe_p
+ libopusenc.ope_encoder_create_pull.argtypes = [ooc_p, opus_int32, c_int, c_int, c_int_p]
+
+ def ope_encoder_create_pull(comments, rate, channels, family, error):
+ return libopusenc.ope_encoder_create_pull(comments, rate, channels, family, error)
+
+ libopusenc.ope_encoder_write_float.restype = c_int
+ libopusenc.ope_encoder_write_float.argtypes = [ooe_p, c_float_p, c_int]
+
+ def ope_encoder_write_float(enc, pcm, samples_per_channel):
+ return libopusenc.ope_encoder_write_float(enc, pcm, samples_per_channel)
+
+ libopusenc.ope_encoder_write.restype = c_int
+ libopusenc.ope_encoder_write.argtypes = [ooe_p, opus_int16_p, c_int]
+
+ def ope_encoder_write(enc, pcm, samples_per_channel):
+ return libopusenc.ope_encoder_write(enc, pcm, samples_per_channel)
+
+ libopusenc.ope_encoder_get_page.restype = c_int
+ libopusenc.ope_encoder_get_page.argtypes = [ooe_p, POINTER(c_uchar_p), opus_int32_p, c_int]
+
+ def ope_encoder_get_page(enc, page, len, flush):
+ return libopusenc.ope_encoder_get_page(enc, page, len, flush)
+
+ libopusenc.ope_encoder_drain.restype = c_int
+ libopusenc.ope_encoder_drain.argtypes = [ooe_p]
+
+ def ope_encoder_drain(enc):
+ return libopusenc.ope_encoder_drain(enc)
+
+ libopusenc.ope_encoder_destroy.restype = None
+ libopusenc.ope_encoder_destroy.argtypes = [ooe_p]
+
+ def ope_encoder_destroy(enc):
+ return libopusenc.ope_encoder_destroy(enc)
+
+ libopusenc.ope_encoder_chain_current.restype = c_int
+ libopusenc.ope_encoder_chain_current.argtypes = [ooe_p, ooc_p]
+
+ def ope_encoder_chain_current(enc, comments):
+ return libopusenc.ope_encoder_chain_current(enc, comments)
+
+ libopusenc.ope_encoder_continue_new_file.restype = c_int
+ libopusenc.ope_encoder_continue_new_file.argtypes = [ooe_p, c_char_p, ooc_p]
+
+ def ope_encoder_continue_new_file(enc,path, comments):
+ return libopusenc.ope_encoder_continue_new_file(enc,path, comments)
+
+ libopusenc.ope_encoder_continue_new_callbacks.restype = c_int
+ libopusenc.ope_encoder_continue_new_callbacks.argtypes = [ooe_p, c_void_p, ooc_p]
+
+ def ope_encoder_continue_new_callbacks(enc,user_data, comments):
+ return libopusenc.ope_encoder_continue_new_callbacks(enc,user_data, comments)
+
+ libopusenc.ope_encoder_flush_header.restype = c_int
+ libopusenc.ope_encoder_flush_header.argtypes = [ooe_p]
+
+ def ope_encoder_flush_header(enc):
+ return libopusenc.ope_encoder_flush_header(enc)
+
+ libopusenc.ope_encoder_ctl.restype = c_int
+ libopusenc.ope_encoder_ctl.argtypes = [ooe_p, c_int]
+
+ def ope_encoder_ctl(enc, request, *args):
+ return libopusenc.ope_encoder_ctl(enc, request, *args)
+
+ libopusenc.ope_strerror.restype = c_char_p
+ libopusenc.ope_strerror.argtypes = [c_int]
+
+ def ope_strerror(error):
+ return libopusenc.ope_strerror(error)
+
+ libopusenc.ope_get_version_string.restype = c_char_p
+ libopusenc.ope_get_version_string.argtypes = []
+
+ def ope_get_version_string():
+ return libopusenc.ope_get_version_string()
+
+ libopusenc.ope_get_abi_version.restype = c_int
+ libopusenc.ope_get_abi_version.argtypes = []
+
+ def ope_get_abi_version():
+ return libopusenc.ope_get_abi_version()
diff --git a/sbapp/pyogg/opus_buffered_encoder.py b/sbapp/pyogg/opus_buffered_encoder.py
new file mode 100644
index 00000000..7179051f
--- /dev/null
+++ b/sbapp/pyogg/opus_buffered_encoder.py
@@ -0,0 +1,407 @@
+import copy
+import ctypes
+from typing import Optional, ByteString, List, Tuple, Callable
+import warnings
+
+from . import opus
+from .opus_encoder import OpusEncoder
+from .pyogg_error import PyOggError
+
+class OpusBufferedEncoder(OpusEncoder):
+ # TODO: This could be made more efficient. We don't need a
+ # deque. Instead, we need only sufficient PCM storage for one
+ # whole packet. We know the size of the packet thanks to
+ # set_frame_size().
+
+ def __init__(self) -> None:
+ super().__init__()
+
+ self._frame_size_ms: Optional[float] = None
+ self._frame_size_bytes: Optional[int] = None
+
+ # Buffer contains the bytes required for the next
+ # frame.
+ self._buffer: Optional[ctypes.Array] = None
+
+ # Location of the next free byte in the buffer
+ self._buffer_index = 0
+
+
+ def set_frame_size(self, frame_size: float) -> None:
+ """ Set the desired frame duration (in milliseconds).
+
+ Valid options are 2.5, 5, 10, 20, 40, or 60ms.
+
+ """
+
+ # Ensure the frame size is valid. Compare frame size in
+ # units of 0.1ms to avoid floating point comparison
+ if int(frame_size*10) not in [25, 50, 100, 200, 400, 600]:
+ raise PyOggError(
+ "Frame size ({:f}) not one of ".format(frame_size)+
+ "the acceptable values"
+ )
+
+ self._frame_size_ms = frame_size
+
+ self._calc_frame_size()
+
+
+ def set_sampling_frequency(self, samples_per_second: int) -> None:
+ super().set_sampling_frequency(samples_per_second)
+ self._calc_frame_size()
+
+
+ def buffered_encode(self,
+ pcm_bytes: memoryview,
+ flush: bool = False,
+ callback: Callable[[memoryview,int,bool],None] = None
+ ) -> List[Tuple[memoryview, int, bool]]:
+ """Gets encoded packets and their number of samples.
+
+ This method returns a list, where each item in the list is
+ a tuple. The first item in the tuple is an Opus-encoded
+ frame stored as a bytes-object. The second item in the
+ tuple is the number of samples encoded (excluding
+ silence).
+
+ If `callback` is supplied then this method will instead
+ return an empty list but call the callback for every
+ Opus-encoded frame that would have been returned as a
+ list. This option has the desireable property of
+ eliminating the copying of the encoded packets, which is
+ required in order to form a list. The callback should
+ take two arguments, the encoded frame (a Python bytes
+ object) and the number of samples encoded per channel (an
+ int). The user must either process or copy the data as
+ the data may be overwritten once the callback terminates.
+
+ """
+ # If there's no work to do return immediately
+ if len(pcm_bytes) == 0 and flush == False:
+ return [] # no work to do
+
+ # Sanity checks
+ if self._frame_size_ms is None:
+ raise PyOggError("Frame size must be set before encoding")
+ assert self._frame_size_bytes is not None
+ assert self._channels is not None
+ assert self._buffer is not None
+ assert self._buffer_index is not None
+
+ # Local variable initialisation
+ results = []
+ pcm_index = 0
+ pcm_len = len(pcm_bytes)
+
+ # 'Cast' memoryview of PCM to ctypes Array
+ Buffer = ctypes.c_ubyte * len(pcm_bytes)
+ try:
+ pcm_ctypes = Buffer.from_buffer(pcm_bytes)
+ except TypeError:
+ warnings.warn(
+ "Because PCM was read-only, an extra memory "+
+ "copy was required; consider storing PCM in "+
+ "writable memory (for example, bytearray "+
+ "rather than bytes)."
+ )
+ pcm_ctypes = Buffer.from_buffer(pcm_bytes)
+
+ # Either store the encoded packet to return at the end of the
+ # method or immediately call the callback with the encoded
+ # packet.
+ def store_or_callback(encoded_packet: memoryview,
+ samples: int,
+ end_of_stream: bool = False) -> None:
+ if callback is None:
+ # Store the result
+ results.append((
+ encoded_packet,
+ samples,
+ end_of_stream
+ ))
+ else:
+ # Call the callback
+ callback(
+ encoded_packet,
+ samples,
+ end_of_stream
+ )
+
+ # Fill the remainder of the buffer with silence and encode it.
+ # The associated number of samples are only that of actual
+ # data, not the added silence.
+ def flush_buffer() -> None:
+ # Sanity checks to satisfy mypy
+ assert self._buffer_index is not None
+ assert self._channels is not None
+ assert self._buffer is not None
+
+ # If the buffer is already empty, we have no work to do
+ if self._buffer_index == 0:
+ return
+
+ # Store the number of samples currently in the buffer
+ samples = (
+ self._buffer_index
+ // self._channels
+ // ctypes.sizeof(opus.opus_int16)
+ )
+
+ # Fill the buffer with silence
+ ctypes.memset(
+ # destination
+ ctypes.byref(self._buffer, self._buffer_index),
+ # value
+ 0,
+ # count
+ len(self._buffer) - self._buffer_index
+ )
+
+ # Encode the PCM
+ # As at 2020-11-05, mypy is unaware that ctype Arrays
+ # support the buffer protocol.
+ encoded_packet = self.encode(memoryview(self._buffer)) # type: ignore
+
+ # Either store the encoded packet or call the
+ # callback
+ store_or_callback(encoded_packet, samples, True)
+
+
+ # Copy the data remaining from the provided PCM into the
+ # buffer. Flush if required.
+ def copy_insufficient_data() -> None:
+ # Sanity checks to satisfy mypy
+ assert self._buffer is not None
+
+ # Calculate remaining data
+ remaining_data = len(pcm_bytes) - pcm_index
+
+ # Copy the data into the buffer.
+ ctypes.memmove(
+ # destination
+ ctypes.byref(self._buffer, self._buffer_index),
+ # source
+ ctypes.byref(pcm_ctypes, pcm_index),
+ # count
+ remaining_data
+ )
+
+ self._buffer_index += remaining_data
+
+ # If we've been asked to flush the buffer then do so
+ if flush:
+ flush_buffer()
+
+ # Loop through the provided PCM and the current buffer,
+ # encoding as we have full packets.
+ while True:
+ # There are two possibilities at this point: either we
+ # have previously unencoded data still in the buffer or we
+ # do not
+ if self._buffer_index == 0:
+ # We do not have unencoded data
+
+ # We are free to progress through the PCM that has
+ # been provided encoding frames without copying any
+ # bytes. Once there is insufficient data remaining
+ # for a complete frame, that data should be copied
+ # into the buffer and we have finished.
+ if pcm_len - pcm_index > self._frame_size_bytes:
+ # We have enough data remaining in the provided
+ # PCM to encode more than an entire frame without
+ # copying any data. Unfortunately, splicing a
+ # ctypes array copies the array. To avoid the
+ # copy we use memoryview see
+ # https://mattgwwalker.wordpress.com/2020/12/12/python-ctypes-slicing/
+ frame_data = memoryview(pcm_bytes)[
+ pcm_index:pcm_index+self._frame_size_bytes
+ ]
+
+ # Update the PCM index
+ pcm_index += self._frame_size_bytes
+
+ # Store number of samples (per channel) of actual
+ # data
+ samples = (
+ len(frame_data)
+ // self._channels
+ // ctypes.sizeof(opus.opus_int16)
+ )
+
+ # Encode the PCM
+ encoded_packet = super().encode(frame_data)
+
+ # Either store the encoded packet or call the
+ # callback
+ store_or_callback(encoded_packet, samples)
+
+ else:
+ # We do not have enough data to fill a frame while
+ # still having data left over. Copy the data into
+ # the buffer.
+ copy_insufficient_data()
+ return results
+
+ else:
+ # We have unencoded data.
+
+ # Copy the provided PCM into the buffer (up until the
+ # buffer is full). If we can fill it, then we can
+ # encode the filled buffer and continue. If we can't
+ # fill it then we've finished.
+ data_required = len(self._buffer) - self._buffer_index
+ if pcm_len > data_required:
+ # We have sufficient data to fill the buffer and
+ # have data left over. Copy data into the buffer.
+ assert pcm_index == 0
+ remaining = len(self._buffer) - self._buffer_index
+ ctypes.memmove(
+ # destination
+ ctypes.byref(self._buffer, self._buffer_index),
+ # source
+ pcm_ctypes,
+ # count
+ remaining
+ )
+ pcm_index += remaining
+ self._buffer_index += remaining
+ assert self._buffer_index == len(self._buffer)
+
+ # Encode the PCM
+ encoded_packet = super().encode(
+ # Memoryviews of ctypes do work, even though
+ # mypy complains.
+ memoryview(self._buffer) # type: ignore
+ )
+
+ # Store number of samples (per channel) of actual
+ # data
+ samples = (
+ self._buffer_index
+ // self._channels
+ // ctypes.sizeof(opus.opus_int16)
+ )
+
+ # We've now processed the buffer
+ self._buffer_index = 0
+
+ # Either store the encoded packet or call the
+ # callback
+ store_or_callback(encoded_packet, samples)
+ else:
+ # We have insufficient data to fill the buffer
+ # while still having data left over. Copy the
+ # data into the buffer.
+ copy_insufficient_data()
+ return results
+
+
+ def _calc_frame_size(self):
+ """Calculates the number of bytes in a frame.
+
+ If the frame size (in milliseconds) and the number of
+ samples per seconds have already been specified, then the
+ frame size in bytes is set. Otherwise, this method does
+ nothing.
+
+ The frame size is measured in bytes required to store the
+ sample.
+
+ """
+ if (self._frame_size_ms is None
+ or self._samples_per_second is None):
+ return
+
+ self._frame_size_bytes = (
+ self._frame_size_ms
+ * self._samples_per_second
+ // 1000
+ * ctypes.sizeof(opus.opus_int16)
+ * self._channels
+ )
+
+ # Allocate space for the buffer
+ Buffer = ctypes.c_ubyte * self._frame_size_bytes
+ self._buffer = Buffer()
+
+
+ def _get_next_frame(self, add_silence=False):
+ """Gets the next Opus-encoded frame.
+
+ Returns a tuple where the first item is the Opus-encoded
+ frame and the second item is the number of encoded samples
+ (per channel).
+
+ Returns None if insufficient data is available.
+
+ """
+ next_frame = bytes()
+ samples = 0
+
+ # Ensure frame size has been specified
+ if self._frame_size_bytes is None:
+ raise PyOggError(
+ "Desired frame size hasn't been set. Perhaps "+
+ "encode() was called before set_frame_size() "+
+ "and set_sampling_frequency()?"
+ )
+
+ # Check if there's insufficient data in the buffer to fill
+ # a frame.
+ if self._frame_size_bytes > self._buffer_size:
+ if len(self._buffer) == 0:
+ # No data at all in buffer
+ return None
+ if add_silence:
+ # Get all remaining data
+ while len(self._buffer) != 0:
+ next_frame += self._buffer.popleft()
+ self._buffer_size = 0
+ # Store number of samples (per channel) of actual
+ # data
+ samples = (
+ len(next_frame)
+ // self._channels
+ // ctypes.sizeof(opus.opus_int16)
+ )
+ # Fill remainder of frame with silence
+ bytes_remaining = self._frame_size_bytes - len(next_frame)
+ next_frame += b'\x00' * bytes_remaining
+ return (next_frame, samples)
+ else:
+ # Insufficient data to fill a frame and we're not
+ # adding silence
+ return None
+
+ bytes_remaining = self._frame_size_bytes
+ while bytes_remaining > 0:
+ if len(self._buffer[0]) <= bytes_remaining:
+ # Take the whole first item
+ buffer_ = self._buffer.popleft()
+ next_frame += buffer_
+ bytes_remaining -= len(buffer_)
+ self._buffer_size -= len(buffer_)
+ else:
+ # Take only part of the buffer
+
+ # TODO: This could be more efficiently
+ # implemented. Rather than appending back the
+ # remaining data, we could just update an index
+ # saying where we were up to in regards to the
+ # first entry of the buffer.
+ buffer_ = self._buffer.popleft()
+ next_frame += buffer_[:bytes_remaining]
+ self._buffer_size -= bytes_remaining
+ # And put the unused part back into the buffer
+ self._buffer.appendleft(buffer_[bytes_remaining:])
+ bytes_remaining = 0
+
+ # Calculate number of samples (per channel)
+ samples = (
+ len(next_frame)
+ // self._channels
+ // ctypes.sizeof(opus.opus_int16)
+ )
+
+ return (next_frame, samples)
diff --git a/sbapp/pyogg/opus_decoder.py b/sbapp/pyogg/opus_decoder.py
new file mode 100644
index 00000000..8a1f4dd3
--- /dev/null
+++ b/sbapp/pyogg/opus_decoder.py
@@ -0,0 +1,273 @@
+import ctypes
+
+from . import opus
+from .pyogg_error import PyOggError
+
+class OpusDecoder:
+ def __init__(self):
+ self._decoder = None
+ self._channels = None
+ self._samples_per_second = None
+ self._pcm_buffer = None
+ self._pcm_buffer_ptr = None
+ self._pcm_buffer_size_int = None
+
+ # TODO: Check if there is clean up that we need to do when
+ # closing a decoder.
+
+ #
+ # User visible methods
+ #
+
+ def set_channels(self, n):
+
+ """Set the number of channels.
+
+ n must be either 1 or 2.
+
+ The decoder is capable of filling in either mono or
+ interleaved stereo pcm buffers.
+
+ """
+ if self._decoder is None:
+ if n < 0 or n > 2:
+ raise PyOggError(
+ "Invalid number of channels in call to "+
+ "set_channels()"
+ )
+ self._channels = n
+ else:
+ raise PyOggError(
+ "Cannot change the number of channels after "+
+ "the decoder was created. Perhaps "+
+ "set_channels() was called after decode()?"
+ )
+ self._create_pcm_buffer()
+
+ def set_sampling_frequency(self, samples_per_second):
+ """Set the number of samples (per channel) per second.
+
+ samples_per_second must be one of 8000, 12000, 16000,
+ 24000, or 48000.
+
+ Internally Opus stores data at 48000 Hz, so that should be
+ the default value for Fs. However, the decoder can
+ efficiently decode to buffers at 8, 12, 16, and 24 kHz so
+ if for some reason the caller cannot use data at the full
+ sample rate, or knows the compressed data doesn't use the
+ full frequency range, it can request decoding at a reduced
+ rate.
+
+ """
+ if self._decoder is None:
+ if samples_per_second in [8000, 12000, 16000, 24000, 48000]:
+ self._samples_per_second = samples_per_second
+ else:
+ raise PyOggError(
+ "Specified sampling frequency "+
+ "({:d}) ".format(samples_per_second)+
+ "was not one of the accepted values"
+ )
+ else:
+ raise PyOggError(
+ "Cannot change the sampling frequency after "+
+ "the decoder was created. Perhaps "+
+ "set_sampling_frequency() was called after decode()?"
+ )
+ self._create_pcm_buffer()
+
+ def decode(self, encoded_bytes: memoryview):
+ """Decodes an Opus-encoded packet into PCM.
+
+ """
+ # If we haven't already created a decoder, do so now
+ if self._decoder is None:
+ self._decoder = self._create_decoder()
+
+ # Create a ctypes array from the memoryview (without copying
+ # data)
+ Buffer = ctypes.c_char * len(encoded_bytes)
+ encoded_bytes_ctypes = Buffer.from_buffer(encoded_bytes)
+
+ # Create pointer to encoded bytes
+ encoded_bytes_ptr = ctypes.cast(
+ encoded_bytes_ctypes,
+ ctypes.POINTER(ctypes.c_ubyte)
+ )
+
+ # Store length of encoded bytes into int32
+ len_int32 = opus.opus_int32(
+ len(encoded_bytes)
+ )
+
+ # Check that we have a PCM buffer
+ if self._pcm_buffer is None:
+ raise PyOggError("PCM buffer was not configured.")
+
+ # Decode the encoded frame
+ result = opus.opus_decode(
+ self._decoder,
+ encoded_bytes_ptr,
+ len_int32,
+ self._pcm_buffer_ptr,
+ self._pcm_buffer_size_int,
+ 0 # TODO: What's Forward Error Correction about?
+ )
+
+ # Check for any errors
+ if result < 0:
+ raise PyOggError(
+ "An error occurred while decoding an Opus-encoded "+
+ "packet: "+
+ opus.opus_strerror(result).decode("utf")
+ )
+
+ # Extract just the valid data as bytes
+ end_valid_data = (
+ result
+ * ctypes.sizeof(opus.opus_int16)
+ * self._channels
+ )
+
+ # Create memoryview of PCM buffer to avoid copying data during slice.
+ mv = memoryview(self._pcm_buffer)
+
+ # Cast memoryview to chars
+ mv = mv.cast('c')
+
+ # Slice memoryview to extract only valid data
+ mv = mv[:end_valid_data]
+
+ return mv
+
+
+ def decode_missing_packet(self, frame_duration):
+ """ Obtain PCM data despite missing a frame.
+
+ frame_duration is in milliseconds.
+
+ """
+
+ # Consider frame duration in units of 0.1ms in order to
+ # avoid floating-point comparisons.
+ if int(frame_duration*10) not in [25, 50, 100, 200, 400, 600]:
+ raise PyOggError(
+ "Frame duration ({:f}) is not one of the accepted values".format(frame_duration)
+ )
+
+ # Calculate frame size
+ frame_size = int(
+ frame_duration
+ * self._samples_per_second
+ // 1000
+ )
+
+ # Store frame size as int
+ frame_size_int = ctypes.c_int(frame_size)
+
+ # Decode missing packet
+ result = opus.opus_decode(
+ self._decoder,
+ None,
+ 0,
+ self._pcm_buffer_ptr,
+ frame_size_int,
+ 0 # TODO: What is this Forward Error Correction about?
+ )
+
+ # Check for any errors
+ if result < 0:
+ raise PyOggError(
+ "An error occurred while decoding an Opus-encoded "+
+ "packet: "+
+ opus.opus_strerror(result).decode("utf")
+ )
+
+ # Extract just the valid data as bytes
+ end_valid_data = (
+ result
+ * ctypes.sizeof(opus.opus_int16)
+ * self._channels
+ )
+ return bytes(self._pcm_buffer)[:end_valid_data]
+
+ #
+ # Internal methods
+ #
+
+ def _create_pcm_buffer(self):
+ if (self._samples_per_second is None
+ or self._channels is None):
+ # We cannot define the buffer yet
+ return
+
+ # Create buffer to hold 120ms of samples. See "opus_decode()" at
+ # https://opus-codec.org/docs/opus_api-1.3.1/group__opus__decoder.html
+ max_duration = 120 # milliseconds
+ max_samples = max_duration * self._samples_per_second // 1000
+ PCMBuffer = opus.opus_int16 * (max_samples * self._channels)
+ self._pcm_buffer = PCMBuffer()
+ self._pcm_buffer_ptr = (
+ ctypes.cast(ctypes.pointer(self._pcm_buffer),
+ ctypes.POINTER(opus.opus_int16))
+ )
+
+ # Store samples per channel in an int
+ self._pcm_buffer_size_int = ctypes.c_int(max_samples)
+
+ def _create_decoder(self):
+ # To create a decoder, we must first allocate resources for it.
+ # We want Python to be responsible for the memory deallocation,
+ # and thus Python must be responsible for the initial memory
+ # allocation.
+
+ # Check that the sampling frequency has been defined
+ if self._samples_per_second is None:
+ raise PyOggError(
+ "The sampling frequency was not specified before "+
+ "attempting to create an Opus decoder. Perhaps "+
+ "decode() was called before set_sampling_frequency()?"
+ )
+
+ # The sampling frequency must be passed in as a 32-bit int
+ samples_per_second = opus.opus_int32(self._samples_per_second)
+
+ # Check that the number of channels has been defined
+ if self._channels is None:
+ raise PyOggError(
+ "The number of channels were not specified before "+
+ "attempting to create an Opus decoder. Perhaps "+
+ "decode() was called before set_channels()?"
+ )
+
+ # The number of channels must also be passed in as a 32-bit int
+ channels = opus.opus_int32(self._channels)
+
+ # Obtain the number of bytes of memory required for the decoder
+ size = opus.opus_decoder_get_size(channels);
+
+ # Allocate the required memory for the decoder
+ memory = ctypes.create_string_buffer(size)
+
+ # Cast the newly-allocated memory as a pointer to a decoder. We
+ # could also have used opus.od_p as the pointer type, but writing
+ # it out in full may be clearer.
+ decoder = ctypes.cast(memory, ctypes.POINTER(opus.OpusDecoder))
+
+ # Initialise the decoder
+ error = opus.opus_decoder_init(
+ decoder,
+ samples_per_second,
+ channels
+ );
+
+ # Check that there hasn't been an error when initialising the
+ # decoder
+ if error != opus.OPUS_OK:
+ raise PyOggError(
+ "An error occurred while creating the decoder: "+
+ opus.opus_strerror(error).decode("utf")
+ )
+
+ # Return our newly-created decoder
+ return decoder
diff --git a/sbapp/pyogg/opus_encoder.py b/sbapp/pyogg/opus_encoder.py
new file mode 100644
index 00000000..1da82daf
--- /dev/null
+++ b/sbapp/pyogg/opus_encoder.py
@@ -0,0 +1,358 @@
+import ctypes
+from typing import Optional, Union, ByteString
+
+from . import opus
+from .pyogg_error import PyOggError
+
+class OpusEncoder:
+ """Encodes PCM data into Opus frames."""
+ def __init__(self) -> None:
+ self._encoder: Optional[ctypes.pointer] = None
+ self._channels: Optional[int] = None
+ self._samples_per_second: Optional[int] = None
+ self._application: Optional[int] = None
+ self._max_bytes_per_frame: Optional[opus.opus_int32] = None
+ self._output_buffer: Optional[ctypes.Array] = None
+ self._output_buffer_ptr: Optional[ctypes.pointer] = None
+
+ # An output buffer of 4,000 bytes is recommended in
+ # https://opus-codec.org/docs/opus_api-1.3.1/group__opus__encoder.html
+ self.set_max_bytes_per_frame(4000)
+
+ #
+ # User visible methods
+ #
+
+ def set_channels(self, n: int) -> None:
+ """Set the number of channels.
+
+ n must be either 1 or 2.
+
+ """
+ if self._encoder is None:
+ if n < 0 or n > 2:
+ raise PyOggError(
+ "Invalid number of channels in call to "+
+ "set_channels()"
+ )
+ self._channels = n
+ else:
+ raise PyOggError(
+ "Cannot change the number of channels after "+
+ "the encoder was created. Perhaps "+
+ "set_channels() was called after encode()?"
+ )
+
+ def set_sampling_frequency(self, samples_per_second: int) -> None:
+ """Set the number of samples (per channel) per second.
+
+ This must be one of 8000, 12000, 16000, 24000, or 48000.
+
+ Regardless of the sampling rate and number of channels
+ selected, the Opus encoder can switch to a lower audio
+ bandwidth or number of channels if the bitrate selected is
+ too low. This also means that it is safe to always use 48
+ kHz stereo input and let the encoder optimize the
+ encoding.
+
+ """
+ if self._encoder is None:
+ if samples_per_second in [8000, 12000, 16000, 24000, 48000]:
+ self._samples_per_second = samples_per_second
+ else:
+ raise PyOggError(
+ "Specified sampling frequency "+
+ "({:d}) ".format(samples_per_second)+
+ "was not one of the accepted values"
+ )
+ else:
+ raise PyOggError(
+ "Cannot change the sampling frequency after "+
+ "the encoder was created. Perhaps "+
+ "set_sampling_frequency() was called after encode()?"
+ )
+
+ def set_application(self, application: str) -> None:
+ """Set the encoding mode.
+
+ This must be one of 'voip', 'audio', or 'restricted_lowdelay'.
+
+ 'voip': Gives best quality at a given bitrate for voice
+ signals. It enhances the input signal by high-pass
+ filtering and emphasizing formants and
+ harmonics. Optionally it includes in-band forward error
+ correction to protect against packet loss. Use this mode
+ for typical VoIP applications. Because of the enhancement,
+ even at high bitrates the output may sound different from
+ the input.
+
+ 'audio': Gives best quality at a given bitrate for most
+ non-voice signals like music. Use this mode for music and
+ mixed (music/voice) content, broadcast, and applications
+ requiring less than 15 ms of coding delay.
+
+ 'restricted_lowdelay': configures low-delay mode that
+ disables the speech-optimized mode in exchange for
+ slightly reduced delay. This mode can only be set on an
+ newly initialized encoder because it changes the codec
+ delay.
+ """
+ if self._encoder is not None:
+ raise PyOggError(
+ "Cannot change the application after "+
+ "the encoder was created. Perhaps "+
+ "set_application() was called after encode()?"
+ )
+ if application == "voip":
+ self._application = opus.OPUS_APPLICATION_VOIP
+ elif application == "audio":
+ self._application = opus.OPUS_APPLICATION_AUDIO
+ elif application == "restricted_lowdelay":
+ self._application = opus.OPUS_APPLICATION_RESTRICTED_LOWDELAY
+ else:
+ raise PyOggError(
+ "The application specification '{:s}' ".format(application)+
+ "wasn't one of the accepted values."
+ )
+
+ def set_max_bytes_per_frame(self, max_bytes: int) -> None:
+ """Set the maximum number of bytes in an encoded frame.
+
+ Size of the output payload. This may be used to impose an
+ upper limit on the instant bitrate, but should not be used
+ as the only bitrate control.
+
+ TODO: Use OPUS_SET_BITRATE to control the bitrate.
+
+ """
+ self._max_bytes_per_frame = opus.opus_int32(max_bytes)
+ OutputBuffer = ctypes.c_ubyte * max_bytes
+ self._output_buffer = OutputBuffer()
+ self._output_buffer_ptr = (
+ ctypes.cast(ctypes.pointer(self._output_buffer),
+ ctypes.POINTER(ctypes.c_ubyte))
+ )
+
+
+ def encode(self, pcm: Union[bytes, bytearray, memoryview]) -> memoryview:
+ """Encodes PCM data into an Opus frame.
+
+ `pcm` must be formatted as bytes-like, with each sample taking
+ two bytes (signed 16-bit integers; interleaved left, then
+ right channels if in stereo).
+
+ If `pcm` is not writeable, a copy of the array will be made.
+
+ """
+ # If we haven't already created an encoder, do so now
+ if self._encoder is None:
+ self._encoder = self._create_encoder()
+
+ # Sanity checks also satisfy mypy type checking
+ assert self._channels is not None
+ assert self._samples_per_second is not None
+ assert self._output_buffer is not None
+
+ # Calculate the effective frame duration of the given PCM
+ # data. Calculate it in units of 0.1ms in order to avoid
+ # floating point comparisons.
+ bytes_per_sample = 2
+ frame_size = (
+ len(pcm) # bytes
+ // bytes_per_sample
+ // self._channels
+ )
+ frame_duration = (
+ (10*frame_size)
+ // (self._samples_per_second//1000)
+ )
+
+ # Check that we have a valid frame size
+ if int(frame_duration) not in [25, 50, 100, 200, 400, 600]:
+ raise PyOggError(
+ "The effective frame duration ({:.1f} ms) "
+ .format(frame_duration/10)+
+ "was not one of the acceptable values."
+ )
+
+ # Create a ctypes object sharing the memory of the PCM data
+ PcmCtypes = ctypes.c_ubyte * len(pcm)
+ try:
+ # Attempt to share the PCM memory
+
+ # Unfortunately, as at 2020-09-27, the type hinting for
+ # read-only and writeable buffer protocols was a
+ # work-in-progress. The following only works for writable
+ # cases, but the method's parameters include a read-only
+ # possibility (bytes), thus we ignore mypy's error.
+ pcm_ctypes = PcmCtypes.from_buffer(pcm) # type: ignore[arg-type]
+ except TypeError:
+ # The data must be copied if it's not writeable
+ pcm_ctypes = PcmCtypes.from_buffer_copy(pcm)
+
+ # Create a pointer to the PCM data
+ pcm_ptr = ctypes.cast(
+ pcm_ctypes,
+ ctypes.POINTER(opus.opus_int16)
+ )
+
+ # Create an int giving the frame size per channel
+ frame_size_int = ctypes.c_int(frame_size)
+
+ # Encode PCM
+ result = opus.opus_encode(
+ self._encoder,
+ pcm_ptr,
+ frame_size_int,
+ self._output_buffer_ptr,
+ self._max_bytes_per_frame
+ )
+
+ # Check for any errors
+ if result < 0:
+ raise PyOggError(
+ "An error occurred while encoding to Opus format: "+
+ opus.opus_strerror(result).decode("utf")
+ )
+
+ # Get memoryview of buffer so that the slice operation doesn't
+ # copy the data.
+ #
+ # Unfortunately, as at 2020-09-27, the type hints for
+ # memoryview do not include ctype arrays. This is because
+ # there is no currently accepted manner to label a class as
+ # supporting the buffer protocol. However, it's clearly a
+ # work in progress. For more information, see:
+ # * https://bugs.python.org/issue27501
+ # * https://github.com/python/typing/issues/593
+ # * https://github.com/python/typeshed/pull/4232
+ mv = memoryview(self._output_buffer) # type: ignore
+
+ # Cast the memoryview to char
+ mv = mv.cast('c')
+
+ # Slice just the valid data from the memoryview
+ valid_data_as_bytes = mv[:result]
+
+ # DEBUG
+ # Convert memoryview back to ctypes instance
+ Buffer = ctypes.c_ubyte * len(valid_data_as_bytes)
+ buf = Buffer.from_buffer( valid_data_as_bytes )
+
+ # Convert PCM back to pointer and dump 4,000-byte buffer
+ ptr = ctypes.cast(
+ buf,
+ ctypes.POINTER(ctypes.c_ubyte)
+ )
+
+ return valid_data_as_bytes
+
+
+ def get_algorithmic_delay(self):
+ """Gets the total samples of delay added by the entire codec.
+
+ This can be queried by the encoder and then the provided
+ number of samples can be skipped on from the start of the
+ decoder's output to provide time aligned input and
+ output. From the perspective of a decoding application the
+ real data begins this many samples late.
+
+ The decoder contribution to this delay is identical for all
+ decoders, but the encoder portion of the delay may vary from
+ implementation to implementation, version to version, or even
+ depend on the encoder's initial configuration. Applications
+ needing delay compensation should call this method rather than
+ hard-coding a value.
+
+ """
+ # If we haven't already created an encoder, do so now
+ if self._encoder is None:
+ self._encoder = self._create_encoder()
+
+ # Obtain the algorithmic delay of the Opus encoder. See
+ # https://tools.ietf.org/html/rfc7845#page-27
+ delay = opus.opus_int32()
+
+ result = opus.opus_encoder_ctl(
+ self._encoder,
+ opus.OPUS_GET_LOOKAHEAD_REQUEST,
+ ctypes.pointer(delay)
+ )
+ if result != opus.OPUS_OK:
+ raise PyOggError(
+ "Failed to obtain the algorithmic delay of "+
+ "the Opus encoder: "+
+ opus.opus_strerror(result).decode("utf")
+ )
+ delay_samples = delay.value
+ return delay_samples
+
+
+ #
+ # Internal methods
+ #
+
+ def _create_encoder(self) -> ctypes.pointer:
+ # To create an encoder, we must first allocate resources for it.
+ # We want Python to be responsible for the memory deallocation,
+ # and thus Python must be responsible for the initial memory
+ # allocation.
+
+ # Check that the application has been defined
+ if self._application is None:
+ raise PyOggError(
+ "The application was not specified before "+
+ "attempting to create an Opus encoder. Perhaps "+
+ "encode() was called before set_application()?"
+ )
+ application = self._application
+
+ # Check that the sampling frequency has been defined
+ if self._samples_per_second is None:
+ raise PyOggError(
+ "The sampling frequency was not specified before "+
+ "attempting to create an Opus encoder. Perhaps "+
+ "encode() was called before set_sampling_frequency()?"
+ )
+
+ # The frequency must be passed in as a 32-bit int
+ samples_per_second = opus.opus_int32(self._samples_per_second)
+
+ # Check that the number of channels has been defined
+ if self._channels is None:
+ raise PyOggError(
+ "The number of channels were not specified before "+
+ "attempting to create an Opus encoder. Perhaps "+
+ "encode() was called before set_channels()?"
+ )
+ channels = self._channels
+
+ # Obtain the number of bytes of memory required for the encoder
+ size = opus.opus_encoder_get_size(channels);
+
+ # Allocate the required memory for the encoder
+ memory = ctypes.create_string_buffer(size)
+
+ # Cast the newly-allocated memory as a pointer to an encoder. We
+ # could also have used opus.oe_p as the pointer type, but writing
+ # it out in full may be clearer.
+ encoder = ctypes.cast(memory, ctypes.POINTER(opus.OpusEncoder))
+
+ # Initialise the encoder
+ error = opus.opus_encoder_init(
+ encoder,
+ samples_per_second,
+ channels,
+ application
+ )
+
+ # Check that there hasn't been an error when initialising the
+ # encoder
+ if error != opus.OPUS_OK:
+ raise PyOggError(
+ "An error occurred while creating the encoder: "+
+ opus.opus_strerror(error).decode("utf")
+ )
+
+ # Return our newly-created encoder
+ return encoder
diff --git a/sbapp/pyogg/opus_file.py b/sbapp/pyogg/opus_file.py
new file mode 100644
index 00000000..f8519f41
--- /dev/null
+++ b/sbapp/pyogg/opus_file.py
@@ -0,0 +1,106 @@
+import ctypes
+
+from . import ogg
+from . import opus
+from .pyogg_error import PyOggError
+from .audio_file import AudioFile
+
+class OpusFile(AudioFile):
+ def __init__(self, path: str) -> None:
+ # Open the file
+ error = ctypes.c_int()
+ of = opus.op_open_file(
+ ogg.to_char_p(path),
+ ctypes.pointer(error)
+ )
+
+ # Check for errors
+ if error.value != 0:
+ raise PyOggError(
+ ("File '{}' couldn't be opened or doesn't exist. "+
+ "Error code: {}").format(path, error.value)
+ )
+
+ # Extract the number of channels in the newly opened file
+ #: Number of channels in audio file.
+ self.channels = opus.op_channel_count(of, -1)
+
+ # Allocate sufficient memory to store the entire PCM
+ pcm_size = opus.op_pcm_total(of, -1)
+ Buf = opus.opus_int16*(pcm_size*self.channels)
+ buf = Buf()
+
+ # Create a pointer to the newly allocated memory. It
+ # seems we can only do pointer arithmetic on void
+ # pointers. See
+ # https://mattgwwalker.wordpress.com/2020/05/30/pointer-manipulation-in-python/
+ buf_ptr = ctypes.cast(
+ ctypes.pointer(buf),
+ ctypes.c_void_p
+ )
+ assert buf_ptr.value is not None # for mypy
+ buf_ptr_zero = buf_ptr.value
+
+ #: Bytes per sample
+ self.bytes_per_sample = ctypes.sizeof(opus.opus_int16)
+
+ # Read through the entire file, copying the PCM into the
+ # buffer
+ samples = 0
+ while True:
+ # Calculate remaining buffer size
+ remaining_buffer = (
+ len(buf) # int
+ - (buf_ptr.value
+ - buf_ptr_zero) // self.bytes_per_sample
+ )
+
+ # Convert buffer pointer to the desired type
+ ptr = ctypes.cast(
+ buf_ptr,
+ ctypes.POINTER(opus.opus_int16)
+ )
+
+ # Read the next section of PCM
+ ns = opus.op_read(
+ of,
+ ptr,
+ remaining_buffer,
+ ogg.c_int_p()
+ )
+
+ # Check for errors
+ if ns<0:
+ raise PyOggError(
+ "Error while reading OggOpus file. "+
+ "Error code: {}".format(ns)
+ )
+
+ # Increment the pointer
+ buf_ptr.value += (
+ ns
+ * self.bytes_per_sample
+ * self.channels
+ )
+ assert buf_ptr.value is not None # for mypy
+
+ samples += ns
+
+ # Check if we've finished
+ if ns==0:
+ break
+
+ # Close the open file
+ opus.op_free(of)
+
+ # Opus files are always stored at 48k samples per second
+ #: Number of samples per second (per channel). Always 48,000.
+ self.frequency = 48000
+
+ # Cast buffer to a one-dimensional array of chars
+ #: Raw PCM data from audio file.
+ CharBuffer = (
+ ctypes.c_byte
+ * (self.bytes_per_sample * self.channels * pcm_size)
+ )
+ self.buffer = CharBuffer.from_buffer(buf)
diff --git a/sbapp/pyogg/opus_file_stream.py b/sbapp/pyogg/opus_file_stream.py
new file mode 100644
index 00000000..b3e1723a
--- /dev/null
+++ b/sbapp/pyogg/opus_file_stream.py
@@ -0,0 +1,127 @@
+import ctypes
+
+from . import ogg
+from . import opus
+from .pyogg_error import PyOggError
+
+class OpusFileStream:
+ def __init__(self, path):
+ """Opens an OggOpus file as a stream.
+
+ path should be a string giving the filename of the file to
+ open. Unicode file names may not work correctly.
+
+ An exception will be raised if the file cannot be opened
+ correctly.
+
+ """
+ error = ctypes.c_int()
+
+ self.of = opus.op_open_file(ogg.to_char_p(path), ctypes.pointer(error))
+
+ if error.value != 0:
+ self.of = None
+ raise PyOggError("file couldn't be opened or doesn't exist. Error code : {}".format(error.value))
+
+ #: Number of channels in audio file
+ self.channels = opus.op_channel_count(self.of, -1)
+
+ #: Total PCM Length
+ self.pcm_size = opus.op_pcm_total(self.of, -1)
+
+ #: Number of samples per second (per channel)
+ self.frequency = 48000
+
+ # The buffer size should be (per channel) large enough to
+ # hold 120ms (the largest possible Opus frame) at 48kHz.
+ # See https://opus-codec.org/docs/opusfile_api-0.7/group__stream__decoding.html#ga963c917749335e29bb2b698c1cb20a10
+ self.buffer_size = self.frequency // 1000 * 120 * self.channels
+ self.Buf = opus.opus_int16 * self.buffer_size
+ self._buf = self.Buf()
+ self.buffer_ptr = ctypes.cast(
+ ctypes.pointer(self._buf),
+ opus.opus_int16_p
+ )
+
+ #: Bytes per sample
+ self.bytes_per_sample = ctypes.sizeof(opus.opus_int16)
+
+ def __del__(self):
+ if self.of is not None:
+ opus.op_free(self.of)
+
+ def get_buffer(self):
+ """Obtains the next frame of PCM samples.
+
+ Returns an array of signed 16-bit integers. If the file
+ is in stereo, the left and right channels are interleaved.
+
+ Returns None when all data has been read.
+
+ The array that is returned should be either processed or
+ copied before the next call to :meth:`~get_buffer` or
+ :meth:`~get_buffer_as_array` as the array's memory is reused for
+ each call.
+
+ """
+ # Read the next frame
+ samples_read = opus.op_read(
+ self.of,
+ self.buffer_ptr,
+ self.buffer_size,
+ None
+ )
+
+ # Check for errors
+ if samples_read < 0:
+ raise PyOggError(
+ "Failed to read OpusFileStream. Error {:d}".format(samples_read)
+ )
+
+ # Check if we've reached the end of the stream
+ if samples_read == 0:
+ return None
+
+ # Cast the pointer to opus_int16 to an array of the
+ # correct size
+ result_ptr = ctypes.cast(
+ self.buffer_ptr,
+ ctypes.POINTER(opus.opus_int16 * (samples_read*self.channels))
+ )
+
+ # Convert the array to Python bytes
+ return bytes(result_ptr.contents)
+
+ def get_buffer_as_array(self):
+ """Provides the buffer as a NumPy array.
+
+ Note that the underlying data type is 16-bit signed
+ integers.
+
+ Does not copy the underlying data, so the returned array
+ should either be processed or copied before the next call
+ to :meth:`~get_buffer` or :meth:`~get_buffer_as_array`.
+
+ """
+ import numpy # type: ignore
+
+ # Read the next samples from the stream
+ buf = self.get_buffer()
+
+ # Check if we've come to the end of the stream
+ if buf is None:
+ return None
+
+ # Convert the bytes buffer to a NumPy array
+ array = numpy.frombuffer(
+ buf,
+ dtype=numpy.int16
+ )
+
+ # Reshape the array
+ return array.reshape(
+ (len(buf)
+ // self.bytes_per_sample
+ // self.channels,
+ self.channels)
+ )
diff --git a/sbapp/pyogg/py.typed b/sbapp/pyogg/py.typed
new file mode 100644
index 00000000..d4defd9b
--- /dev/null
+++ b/sbapp/pyogg/py.typed
@@ -0,0 +1 @@
+# Marker file for PEP 561. This package uses inline types.
\ No newline at end of file
diff --git a/sbapp/pyogg/pyogg_error.py b/sbapp/pyogg/pyogg_error.py
new file mode 100644
index 00000000..35f28bf1
--- /dev/null
+++ b/sbapp/pyogg/pyogg_error.py
@@ -0,0 +1,2 @@
+class PyOggError(Exception):
+ pass
diff --git a/sbapp/pyogg/vorbis.py b/sbapp/pyogg/vorbis.py
new file mode 100644
index 00000000..a8432ba2
--- /dev/null
+++ b/sbapp/pyogg/vorbis.py
@@ -0,0 +1,855 @@
+############################################################
+# Vorbis license: #
+############################################################
+"""
+Copyright (c) 2002-2015 Xiph.org Foundation
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+- Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+- Neither the name of the Xiph.org Foundation nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""
+
+import ctypes
+import ctypes.util
+from traceback import print_exc as _print_exc
+import os
+
+OV_EXCLUDE_STATIC_CALLBACKS = False
+
+__MINGW32__ = False
+
+_WIN32 = False
+
+from .ogg import *
+
+from .library_loader import ExternalLibrary, ExternalLibraryError
+
+__here = os.getcwd()
+
+libvorbis = None
+
+try:
+ names = {
+ "Windows": "libvorbis.dll",
+ "Darwin": "libvorbis.0.dylib",
+ "external": "vorbis"
+ }
+ libvorbis = Library.load(names, tests = [lambda lib: hasattr(lib, "vorbis_info_init")])
+except ExternalLibraryError:
+ pass
+except:
+ _print_exc()
+
+libvorbisfile = None
+
+try:
+ names = {
+ "Windows": "libvorbisfile.dll",
+ "Darwin": "libvorbisfile.3.dylib",
+ "external": "vorbisfile"
+ }
+ libvorbisfile = Library.load(names, tests = [lambda lib: hasattr(lib, "ov_clear")])
+except ExternalLibraryError:
+ pass
+except:
+ _print_exc()
+
+libvorbisenc = None
+
+# In some cases, libvorbis may also have the libvorbisenc functionality.
+libvorbis_is_also_libvorbisenc = True
+
+for f in ("vorbis_encode_ctl",
+ "vorbis_encode_init",
+ "vorbis_encode_init_vbr",
+ "vorbis_encode_setup_init",
+ "vorbis_encode_setup_managed",
+ "vorbis_encode_setup_vbr"):
+ if not hasattr(libvorbis, f):
+ libvorbis_is_also_libvorbisenc = False
+ break
+
+if libvorbis_is_also_libvorbisenc:
+ libvorbisenc = libvorbis
+else:
+ try:
+ names = {
+ "Windows": "libvorbisenc.dll",
+ "Darwin": "libvorbisenc.2.dylib",
+ "external": "vorbisenc"
+ }
+ libvorbisenc = Library.load(names, tests = [lambda lib: hasattr(lib, "vorbis_encode_init")])
+ except ExternalLibraryError:
+ pass
+ except:
+ _print_exc()
+
+if libvorbis is None:
+ PYOGG_VORBIS_AVAIL = False
+else:
+ PYOGG_VORBIS_AVAIL = True
+
+if libvorbisfile is None:
+ PYOGG_VORBIS_FILE_AVAIL = False
+else:
+ PYOGG_VORBIS_FILE_AVAIL = True
+
+if libvorbisenc is None:
+ PYOGG_VORBIS_ENC_AVAIL = False
+else:
+ PYOGG_VORBIS_ENC_AVAIL = True
+
+# FIXME: What's the story with the lack of checking for PYOGG_VORBIS_ENC_AVAIL?
+# We just seem to assume that it's available.
+
+if PYOGG_OGG_AVAIL and PYOGG_VORBIS_AVAIL and PYOGG_VORBIS_FILE_AVAIL:
+ # Sanity check also satisfies mypy type checking
+ assert libogg is not None
+ assert libvorbis is not None
+ assert libvorbisfile is not None
+
+
+ # codecs
+ class vorbis_info(ctypes.Structure):
+ """
+ Wrapper for:
+ typedef struct vorbis_info vorbis_info;
+ """
+ _fields_ = [("version", c_int),
+ ("channels", c_int),
+ ("rate", c_long),
+
+ ("bitrate_upper", c_long),
+ ("bitrate_nominal", c_long),
+ ("bitrate_lower", c_long),
+ ("bitrate_window", c_long),
+ ("codec_setup", c_void_p)]
+
+
+
+ class vorbis_dsp_state(ctypes.Structure):
+ """
+ Wrapper for:
+ typedef struct vorbis_dsp_state vorbis_dsp_state;
+ """
+ _fields_ = [("analysisp", c_int),
+ ("vi", POINTER(vorbis_info)),
+ ("pcm", c_float_p_p),
+ ("pcmret", c_float_p_p),
+ ("pcm_storage", c_int),
+ ("pcm_current", c_int),
+ ("pcm_returned", c_int),
+
+ ("preextrapolate", c_int),
+ ("eofflag", c_int),
+
+ ("lW", c_long),
+ ("W", c_long),
+ ("nW", c_long),
+ ("centerW", c_long),
+
+ ("granulepos", ogg_int64_t),
+ ("sequence", ogg_int64_t),
+
+ ("glue_bits", ogg_int64_t),
+ ("time_bits", ogg_int64_t),
+ ("floor_bits", ogg_int64_t),
+ ("res_bits", ogg_int64_t),
+
+ ("backend_state", c_void_p)]
+
+ class alloc_chain(ctypes.Structure):
+ """
+ Wrapper for:
+ typedef struct alloc_chain;
+ """
+ pass
+
+ alloc_chain._fields_ = [("ptr", c_void_p),
+ ("next", POINTER(alloc_chain))]
+
+ class vorbis_block(ctypes.Structure):
+ """
+ Wrapper for:
+ typedef struct vorbis_block vorbis_block;
+ """
+ _fields_ = [("pcm", c_float_p_p),
+ ("opb", oggpack_buffer),
+ ("lW", c_long),
+ ("W", c_long),
+ ("nW", c_long),
+ ("pcmend", c_int),
+ ("mode", c_int),
+
+ ("eofflag", c_int),
+ ("granulepos", ogg_int64_t),
+ ("sequence", ogg_int64_t),
+ ("vd", POINTER(vorbis_dsp_state)),
+
+ ("localstore", c_void_p),
+ ("localtop", c_long),
+ ("localalloc", c_long),
+ ("totaluse", c_long),
+ ("reap", POINTER(alloc_chain)),
+
+ ("glue_bits", c_long),
+ ("time_bits", c_long),
+ ("floor_bits", c_long),
+ ("res_bits", c_long),
+
+ ("internal", c_void_p)]
+
+ class vorbis_comment(ctypes.Structure):
+ """
+ Wrapper for:
+ typedef struct vorbis_comment vorbis_comment;
+ """
+ _fields_ = [("user_comments", c_char_p_p),
+ ("comment_lengths", c_int_p),
+ ("comments", c_int),
+ ("vendor", c_char_p)]
+
+
+
+ vi_p = POINTER(vorbis_info)
+ vc_p = POINTER(vorbis_comment)
+ vd_p = POINTER(vorbis_dsp_state)
+ vb_p = POINTER(vorbis_block)
+
+ libvorbis.vorbis_info_init.restype = None
+ libvorbis.vorbis_info_init.argtypes = [vi_p]
+ def vorbis_info_init(vi):
+ libvorbis.vorbis_info_init(vi)
+
+ libvorbis.vorbis_info_clear.restype = None
+ libvorbis.vorbis_info_clear.argtypes = [vi_p]
+ def vorbis_info_clear(vi):
+ libvorbis.vorbis_info_clear(vi)
+
+ libvorbis.vorbis_info_blocksize.restype = c_int
+ libvorbis.vorbis_info_blocksize.argtypes = [vi_p, c_int]
+ def vorbis_info_blocksize(vi, zo):
+ return libvorbis.vorbis_info_blocksize(vi, zo)
+
+ libvorbis.vorbis_comment_init.restype = None
+ libvorbis.vorbis_comment_init.argtypes = [vc_p]
+ def vorbis_comment_init(vc):
+ libvorbis.vorbis_comment_init(vc)
+
+ libvorbis.vorbis_comment_add.restype = None
+ libvorbis.vorbis_comment_add.argtypes = [vc_p, c_char_p]
+ def vorbis_comment_add(vc, comment):
+ libvorbis.vorbis_comment_add(vc, comment)
+
+ libvorbis.vorbis_comment_add_tag.restype = None
+ libvorbis.vorbis_comment_add_tag.argtypes = [vc_p, c_char_p, c_char_p]
+ def vorbis_comment_add_tag(vc, tag, comment):
+ libvorbis.vorbis_comment_add_tag(vc, tag, comment)
+
+ libvorbis.vorbis_comment_query.restype = c_char_p
+ libvorbis.vorbis_comment_query.argtypes = [vc_p, c_char_p, c_int]
+ def vorbis_comment_query(vc, tag, count):
+ libvorbis.vorbis_comment_query(vc, tag, count)
+
+ libvorbis.vorbis_comment_query_count.restype = c_int
+ libvorbis.vorbis_comment_query_count.argtypes = [vc_p, c_char_p]
+ def vorbis_comment_query_count(vc, tag):
+ libvorbis.vorbis_comment_query_count(vc, tag)
+
+ libvorbis.vorbis_comment_clear.restype = None
+ libvorbis.vorbis_comment_clear.argtypes = [vc_p]
+ def vorbis_comment_clear(vc):
+ libvorbis.vorbis_comment_clear(vc)
+
+
+
+ libvorbis.vorbis_block_init.restype = c_int
+ libvorbis.vorbis_block_init.argtypes = [vd_p, vb_p]
+ def vorbis_block_init(v,vb):
+ return libvorbis.vorbis_block_init(v,vb)
+
+ libvorbis.vorbis_block_clear.restype = c_int
+ libvorbis.vorbis_block_clear.argtypes = [vb_p]
+ def vorbis_block_clear(vb):
+ return libvorbis.vorbis_block_clear(vb)
+
+ libvorbis.vorbis_dsp_clear.restype = None
+ libvorbis.vorbis_dsp_clear.argtypes = [vd_p]
+ def vorbis_dsp_clear(v):
+ return libvorbis.vorbis_dsp_clear(v)
+
+ libvorbis.vorbis_granule_time.restype = c_double
+ libvorbis.vorbis_granule_time.argtypes = [vd_p, ogg_int64_t]
+ def vorbis_granule_time(v, granulepos):
+ return libvorbis.vorbis_granule_time(v, granulepos)
+
+
+
+ libvorbis.vorbis_version_string.restype = c_char_p
+ libvorbis.vorbis_version_string.argtypes = []
+ def vorbis_version_string():
+ return libvorbis.vorbis_version_string()
+
+
+
+
+
+ libvorbis.vorbis_analysis_init.restype = c_int
+ libvorbis.vorbis_analysis_init.argtypes = [vd_p, vi_p]
+ def vorbis_analysis_init(v, vi):
+ return libvorbis.vorbis_analysis_init(v, vi)
+
+ libvorbis.vorbis_commentheader_out.restype = c_int
+ libvorbis.vorbis_commentheader_out.argtypes = [vc_p, op_p]
+ def vorbis_commentheader_out(vc, op):
+ return libvorbis.vorbis_commentheader_out(vc, op)
+
+ libvorbis.vorbis_analysis_headerout.restype = c_int
+ libvorbis.vorbis_analysis_headerout.argtypes = [vd_p, vc_p, op_p, op_p, op_p]
+ def vorbis_analysis_headerout(v,vc, op, op_comm, op_code):
+ return libvorbis.vorbis_analysis_headerout(v,vc, op, op_comm, op_code)
+
+ libvorbis.vorbis_analysis_buffer.restype = c_float_p_p
+ libvorbis.vorbis_analysis_buffer.argtypes = [vd_p, c_int]
+ def vorbis_analysis_buffer(v, vals):
+ return libvorbis.vorbis_analysis_buffer(v, vals)
+
+ libvorbis.vorbis_analysis_wrote.restype = c_int
+ libvorbis.vorbis_analysis_wrote.argtypes = [vd_p, c_int]
+ def vorbis_analysis_wrote(v, vals):
+ return libvorbis.vorbis_analysis_wrote(v, vals)
+
+ libvorbis.vorbis_analysis_blockout.restype = c_int
+ libvorbis.vorbis_analysis_blockout.argtypes = [vd_p, vb_p]
+ def vorbis_analysis_blockout(v, vb):
+ return libvorbis.vorbis_analysis_blockout(v, vb)
+
+ libvorbis.vorbis_analysis.restype = c_int
+ libvorbis.vorbis_analysis.argtypes = [vb_p, op_p]
+ def vorbis_analysis(vb, op):
+ return libvorbis.vorbis_analysis(vb, op)
+
+
+
+
+ libvorbis.vorbis_bitrate_addblock.restype = c_int
+ libvorbis.vorbis_bitrate_addblock.argtypes = [vb_p]
+ def vorbis_bitrate_addblock(vb):
+ return libvorbis.vorbis_bitrate_addblock(vb)
+
+ libvorbis.vorbis_bitrate_flushpacket.restype = c_int
+ libvorbis.vorbis_bitrate_flushpacket.argtypes = [vd_p, op_p]
+ def vorbis_bitrate_flushpacket(vd, op):
+ return libvorbis.vorbis_bitrate_flushpacket(vd, op)
+
+
+
+
+ libvorbis.vorbis_synthesis_idheader.restype = c_int
+ libvorbis.vorbis_synthesis_idheader.argtypes = [op_p]
+ def vorbis_synthesis_idheader(op):
+ return libvorbis.vorbis_synthesis_idheader(op)
+
+ libvorbis.vorbis_synthesis_headerin.restype = c_int
+ libvorbis.vorbis_synthesis_headerin.argtypes = [vi_p, vc_p, op_p]
+ def vorbis_synthesis_headerin(vi, vc, op):
+ return libvorbis.vorbis_synthesis_headerin(vi, vc, op)
+
+
+
+
+ libvorbis.vorbis_synthesis_init.restype = c_int
+ libvorbis.vorbis_synthesis_init.argtypes = [vd_p, vi_p]
+ def vorbis_synthesis_init(v,vi):
+ return libvorbis.vorbis_synthesis_init(v,vi)
+
+ libvorbis.vorbis_synthesis_restart.restype = c_int
+ libvorbis.vorbis_synthesis_restart.argtypes = [vd_p]
+ def vorbis_synthesis_restart(v):
+ return libvorbis.vorbis_synthesis_restart(v)
+
+ libvorbis.vorbis_synthesis.restype = c_int
+ libvorbis.vorbis_synthesis.argtypes = [vb_p, op_p]
+ def vorbis_synthesis(vb, op):
+ return libvorbis.vorbis_synthesis(vb, op)
+
+ libvorbis.vorbis_synthesis_trackonly.restype = c_int
+ libvorbis.vorbis_synthesis_trackonly.argtypes = [vb_p, op_p]
+ def vorbis_synthesis_trackonly(vb, op):
+ return libvorbis.vorbis_synthesis_trackonly(vb, op)
+
+ libvorbis.vorbis_synthesis_blockin.restype = c_int
+ libvorbis.vorbis_synthesis_blockin.argtypes = [vd_p, vb_p]
+ def vorbis_synthesis_blockin(v, vb):
+ return libvorbis.vorbis_synthesis_blockin(v, vb)
+
+ libvorbis.vorbis_synthesis_pcmout.restype = c_int
+ libvorbis.vorbis_synthesis_pcmout.argtypes = [vd_p, c_float_p_p_p]
+ def vorbis_synthesis_pcmout(v, pcm):
+ return libvorbis.vorbis_synthesis_pcmout(v, pcm)
+
+ libvorbis.vorbis_synthesis_lapout.restype = c_int
+ libvorbis.vorbis_synthesis_lapout.argtypes = [vd_p, c_float_p_p_p]
+ def vorbis_synthesis_lapout(v, pcm):
+ return libvorbis.vorbis_synthesis_lapout(v, pcm)
+
+ libvorbis.vorbis_synthesis_read.restype = c_int
+ libvorbis.vorbis_synthesis_read.argtypes = [vd_p, c_int]
+ def vorbis_synthesis_read(v, samples):
+ return libvorbis.vorbis_synthesis_read(v, samples)
+
+ libvorbis.vorbis_packet_blocksize.restype = c_long
+ libvorbis.vorbis_packet_blocksize.argtypes = [vi_p, op_p]
+ def vorbis_packet_blocksize(vi, op):
+ return libvorbis.vorbis_packet_blocksize(vi, op)
+
+
+
+ libvorbis.vorbis_synthesis_halfrate.restype = c_int
+ libvorbis.vorbis_synthesis_halfrate.argtypes = [vi_p, c_int]
+ def vorbis_synthesis_halfrate(v, flag):
+ return libvorbis.vorbis_synthesis_halfrate(v, flag)
+
+ libvorbis.vorbis_synthesis_halfrate_p.restype = c_int
+ libvorbis.vorbis_synthesis_halfrate_p.argtypes = [vi_p]
+ def vorbis_synthesis_halfrate_p(vi):
+ return libvorbis.vorbis_synthesis_halfrate_p(vi)
+
+ OV_FALSE = -1
+ OV_EOF = -2
+ OV_HOLE = -3
+
+ OV_EREAD = -128
+ OV_EFAULT = -129
+ OV_EIMPL =-130
+ OV_EINVAL =-131
+ OV_ENOTVORBIS =-132
+ OV_EBADHEADER =-133
+ OV_EVERSION =-134
+ OV_ENOTAUDIO =-135
+ OV_EBADPACKET =-136
+ OV_EBADLINK =-137
+ OV_ENOSEEK =-138
+ # end of codecs
+
+ # vorbisfile
+ read_func = ctypes.CFUNCTYPE(c_size_t,
+ c_void_p,
+ c_size_t,
+ c_size_t,
+ c_void_p)
+
+ seek_func = ctypes.CFUNCTYPE(c_int,
+ c_void_p,
+ ogg_int64_t,
+ c_int)
+
+ close_func = ctypes.CFUNCTYPE(c_int,
+ c_void_p)
+
+ tell_func = ctypes.CFUNCTYPE(c_long,
+ c_void_p)
+
+ class ov_callbacks(ctypes.Structure):
+ """
+ Wrapper for:
+ typedef struct ov_callbacks;
+ """
+
+ _fields_ = [("read_func", read_func),
+ ("seek_func", seek_func),
+ ("close_func", close_func),
+ ("tell_func", tell_func)]
+
+ NOTOPEN = 0
+ PARTOPEN = 1
+ OPENED = 2
+ STREAMSET = 3
+ INITSET = 4
+
+ class OggVorbis_File(ctypes.Structure):
+ """
+ Wrapper for:
+ typedef struct OggVorbis_File OggVorbis_File;
+ """
+
+ _fields_ = [("datasource", c_void_p),
+ ("seekable", c_int),
+ ("offset", ogg_int64_t),
+ ("end", ogg_int64_t),
+ ("oy", ogg_sync_state),
+
+ ("links", c_int),
+ ("offsets", ogg_int64_t_p),
+ ("dataoffsets", ogg_int64_t_p),
+ ("serialnos", c_long_p),
+ ("pcmlengths", ogg_int64_t_p),
+ ("vi", vi_p),
+ ("vc", vc_p),
+
+ ("pcm_offset", ogg_int64_t),
+ ("ready_state", c_int),
+ ("current_serialno", c_long),
+ ("current_link", c_int),
+
+ ("bittrack", c_double),
+ ("samptrack", c_double),
+
+ ("os", ogg_stream_state),
+
+ ("vd", vorbis_dsp_state),
+ ("vb", vorbis_block),
+
+ ("callbacks", ov_callbacks)]
+ vf_p = POINTER(OggVorbis_File)
+
+ libvorbisfile.ov_clear.restype = c_int
+ libvorbisfile.ov_clear.argtypes = [vf_p]
+
+ def ov_clear(vf):
+ return libvorbisfile.ov_clear(vf)
+
+ libvorbisfile.ov_fopen.restype = c_int
+ libvorbisfile.ov_fopen.argtypes = [c_char_p, vf_p]
+
+ def ov_fopen(path, vf):
+ return libvorbisfile.ov_fopen(to_char_p(path), vf)
+
+ libvorbisfile.ov_open_callbacks.restype = c_int
+ libvorbisfile.ov_open_callbacks.argtypes = [c_void_p, vf_p, c_char_p, c_long, ov_callbacks]
+
+ def ov_open_callbacks(datasource, vf, initial, ibytes, callbacks):
+ return libvorbisfile.ov_open_callbacks(datasource, vf, initial, ibytes, callbacks)
+
+ def ov_open(*args, **kw):
+ raise PyOggError("ov_open is not supported, please use ov_fopen instead")
+
+ def ov_test(*args, **kw):
+ raise PyOggError("ov_test is not supported")
+
+ libvorbisfile.ov_test_callbacks.restype = c_int
+ libvorbisfile.ov_test_callbacks.argtypes = [c_void_p, vf_p, c_char_p, c_long, ov_callbacks]
+
+ def ov_test_callbacks(datasource, vf, initial, ibytes, callbacks):
+ return libvorbisfile.ov_test_callbacks(datasource, vf, initial, ibytes, callbacks)
+
+ libvorbisfile.ov_test_open.restype = c_int
+ libvorbisfile.ov_test_open.argtypes = [vf_p]
+
+ def ov_test_open(vf):
+ return libvorbisfile.ov_test_open(vf)
+
+
+
+
+ libvorbisfile.ov_bitrate.restype = c_long
+ libvorbisfile.ov_bitrate.argtypes = [vf_p, c_int]
+
+ def ov_bitrate(vf, i):
+ return libvorbisfile.ov_bitrate(vf, i)
+
+ libvorbisfile.ov_bitrate_instant.restype = c_long
+ libvorbisfile.ov_bitrate_instant.argtypes = [vf_p]
+
+ def ov_bitrate_instant(vf):
+ return libvorbisfile.ov_bitrate_instant(vf)
+
+ libvorbisfile.ov_streams.restype = c_long
+ libvorbisfile.ov_streams.argtypes = [vf_p]
+
+ def ov_streams(vf):
+ return libvorbisfile.ov_streams(vf)
+
+ libvorbisfile.ov_seekable.restype = c_long
+ libvorbisfile.ov_seekable.argtypes = [vf_p]
+
+ def ov_seekable(vf):
+ return libvorbisfile.ov_seekable(vf)
+
+ libvorbisfile.ov_serialnumber.restype = c_long
+ libvorbisfile.ov_serialnumber.argtypes = [vf_p, c_int]
+
+ def ov_serialnumber(vf, i):
+ return libvorbisfile.ov_serialnumber(vf, i)
+
+
+
+ libvorbisfile.ov_raw_total.restype = ogg_int64_t
+ libvorbisfile.ov_raw_total.argtypes = [vf_p, c_int]
+
+ def ov_raw_total(vf, i):
+ return libvorbisfile.ov_raw_total(vf, i)
+
+ libvorbisfile.ov_pcm_total.restype = ogg_int64_t
+ libvorbisfile.ov_pcm_total.argtypes = [vf_p, c_int]
+
+ def ov_pcm_total(vf, i):
+ return libvorbisfile.ov_pcm_total(vf, i)
+
+ libvorbisfile.ov_time_total.restype = c_double
+ libvorbisfile.ov_time_total.argtypes = [vf_p, c_int]
+
+ def ov_time_total(vf, i):
+ return libvorbisfile.ov_time_total(vf, i)
+
+
+
+
+ libvorbisfile.ov_raw_seek.restype = c_int
+ libvorbisfile.ov_raw_seek.argtypes = [vf_p, ogg_int64_t]
+
+ def ov_raw_seek(vf, pos):
+ return libvorbisfile.ov_raw_seek(vf, pos)
+
+ libvorbisfile.ov_pcm_seek.restype = c_int
+ libvorbisfile.ov_pcm_seek.argtypes = [vf_p, ogg_int64_t]
+
+ def ov_pcm_seek(vf, pos):
+ return libvorbisfile.ov_pcm_seek(vf, pos)
+
+ libvorbisfile.ov_pcm_seek_page.restype = c_int
+ libvorbisfile.ov_pcm_seek_page.argtypes = [vf_p, ogg_int64_t]
+
+ def ov_pcm_seek_page(vf, pos):
+ return libvorbisfile.ov_pcm_seek_page(vf, pos)
+
+ libvorbisfile.ov_time_seek.restype = c_int
+ libvorbisfile.ov_time_seek.argtypes = [vf_p, c_double]
+
+ def ov_time_seek(vf, pos):
+ return libvorbisfile.ov_time_seek(vf, pos)
+
+ libvorbisfile.ov_time_seek_page.restype = c_int
+ libvorbisfile.ov_time_seek_page.argtypes = [vf_p, c_double]
+
+ def ov_time_seek_page(vf, pos):
+ return libvorbisfile.ov_time_seek_page(vf, pos)
+
+
+
+
+ libvorbisfile.ov_raw_seek_lap.restype = c_int
+ libvorbisfile.ov_raw_seek_lap.argtypes = [vf_p, ogg_int64_t]
+
+ def ov_raw_seek_lap(vf, pos):
+ return libvorbisfile.ov_raw_seek_lap(vf, pos)
+
+ libvorbisfile.ov_pcm_seek_lap.restype = c_int
+ libvorbisfile.ov_pcm_seek_lap.argtypes = [vf_p, ogg_int64_t]
+
+ def ov_pcm_seek_lap(vf, pos):
+ return libvorbisfile.ov_pcm_seek_lap(vf, pos)
+
+ libvorbisfile.ov_pcm_seek_page_lap.restype = c_int
+ libvorbisfile.ov_pcm_seek_page_lap.argtypes = [vf_p, ogg_int64_t]
+
+ def ov_pcm_seek_page_lap(vf, pos):
+ return libvorbisfile.ov_pcm_seek_page_lap(vf, pos)
+
+ libvorbisfile.ov_time_seek_lap.restype = c_int
+ libvorbisfile.ov_time_seek_lap.argtypes = [vf_p, c_double]
+
+ def ov_time_seek_lap(vf, pos):
+ return libvorbisfile.ov_time_seek_lap(vf, pos)
+
+ libvorbisfile.ov_time_seek_page_lap.restype = c_int
+ libvorbisfile.ov_time_seek_page_lap.argtypes = [vf_p, c_double]
+
+ def ov_time_seek_page_lap(vf, pos):
+ return libvorbisfile.ov_time_seek_page_lap(vf, pos)
+
+
+
+ libvorbisfile.ov_raw_tell.restype = ogg_int64_t
+ libvorbisfile.ov_raw_tell.argtypes = [vf_p]
+
+ def ov_raw_tell(vf):
+ return libvorbisfile.ov_raw_tell(vf)
+
+ libvorbisfile.ov_pcm_tell.restype = ogg_int64_t
+ libvorbisfile.ov_pcm_tell.argtypes = [vf_p]
+
+ def ov_pcm_tell(vf):
+ return libvorbisfile.ov_pcm_tell(vf)
+
+ libvorbisfile.ov_time_tell.restype = c_double
+ libvorbisfile.ov_time_tell.argtypes = [vf_p]
+
+ def ov_time_tell(vf):
+ return libvorbisfile.ov_time_tell(vf)
+
+
+
+ libvorbisfile.ov_info.restype = vi_p
+ libvorbisfile.ov_info.argtypes = [vf_p, c_int]
+
+ def ov_info(vf, link):
+ return libvorbisfile.ov_info(vf, link)
+
+ libvorbisfile.ov_comment.restype = vc_p
+ libvorbisfile.ov_comment.argtypes = [vf_p, c_int]
+
+ def ov_comment(vf, link):
+ return libvorbisfile.ov_comment(vf, link)
+
+
+
+ libvorbisfile.ov_read_float.restype = c_long
+ libvorbisfile.ov_read_float.argtypes = [vf_p, c_float_p_p_p, c_int, c_int_p]
+
+ def ov_read_float(vf, pcm_channels, samples, bitstream):
+ return libvorbisfile.ov_read_float(vf, pcm_channels, samples, bitstream)
+
+ filter_ = ctypes.CFUNCTYPE(None,
+ c_float_p_p,
+ c_long,
+ c_long,
+ c_void_p)
+
+ try:
+ libvorbisfile.ov_read_filter.restype = c_long
+ libvorbisfile.ov_read_filter.argtypes = [vf_p, c_char_p, c_int, c_int, c_int, c_int, c_int_p, filter_, c_void_p]
+
+ def ov_read_filter(vf, buffer, length, bigendianp, word, sgned, bitstream, filter_, filter_param):
+ return libvorbisfile.ov_read_filter(vf, buffer, length, bigendianp, word, sgned, bitstream, filter_, filter_param)
+ except:
+ pass
+
+ libvorbisfile.ov_read.restype = c_long
+ libvorbisfile.ov_read.argtypes = [vf_p, c_char_p, c_int, c_int, c_int, c_int, c_int_p]
+
+ def ov_read(vf, buffer, length, bigendianp, word, sgned, bitstream):
+ return libvorbisfile.ov_read(vf, buffer, length, bigendianp, word, sgned, bitstream)
+
+ libvorbisfile.ov_crosslap.restype = c_int
+ libvorbisfile.ov_crosslap.argtypes = [vf_p, vf_p]
+
+ def ov_crosslap(vf1, cf2):
+ return libvorbisfile.ov_crosslap(vf1, vf2)
+
+
+
+
+ libvorbisfile.ov_halfrate.restype = c_int
+ libvorbisfile.ov_halfrate.argtypes = [vf_p, c_int]
+
+ def ov_halfrate(vf, flag):
+ return libvorbisfile.ov_halfrate(vf, flag)
+
+ libvorbisfile.ov_halfrate_p.restype = c_int
+ libvorbisfile.ov_halfrate_p.argtypes = [vf_p]
+
+ def ov_halfrate_p(vf):
+ return libvorbisfile.ov_halfrate_p(vf)
+ # end of vorbisfile
+
+ try:
+ # vorbisenc
+
+ # Sanity check also satisfies mypy type checking
+ assert libvorbisenc is not None
+
+ libvorbisenc.vorbis_encode_init.restype = c_int
+ libvorbisenc.vorbis_encode_init.argtypes = [vi_p, c_long, c_long, c_long, c_long, c_long]
+
+ def vorbis_encode_init(vi, channels, rate, max_bitrate, nominal_bitrate, min_bitrate):
+ return libvorbisenc.vorbis_encode_init(vi, channels, rate, max_bitrate, nominal_bitrate, min_bitrate)
+
+ libvorbisenc.vorbis_encode_setup_managed.restype = c_int
+ libvorbisenc.vorbis_encode_setup_managed.argtypes = [vi_p, c_long, c_long, c_long, c_long, c_long]
+
+ def vorbis_encode_setup_managed(vi, channels, rate, max_bitrate, nominal_bitrate, min_bitrate):
+ return libvorbisenc.vorbis_encode_setup_managed(vi, channels, rate, max_bitrate, nominal_bitrate, min_bitrate)
+
+ libvorbisenc.vorbis_encode_setup_vbr.restype = c_int
+ libvorbisenc.vorbis_encode_setup_vbr.argtypes = [vi_p, c_long, c_long, c_float]
+
+ def vorbis_encode_setup_vbr(vi, channels, rate, quality):
+ return libvorbisenc.vorbis_encode_setup_vbr(vi, channels, rate, quality)
+
+ libvorbisenc.vorbis_encode_init_vbr.restype = c_int
+ libvorbisenc.vorbis_encode_init_vbr.argtypes = [vi_p, c_long, c_long, c_float]
+
+ def vorbis_encode_init_vbr(vi, channels, rate, quality):
+ return libvorbisenc.vorbis_encode_init_vbr(vi, channels, rate, quality)
+
+ libvorbisenc.vorbis_encode_setup_init.restype = c_int
+ libvorbisenc.vorbis_encode_setup_init.argtypes = [vi_p]
+
+ def vorbis_encode_setup_init(vi):
+ return libvorbisenc.vorbis_encode_setup_init(vi)
+
+ libvorbisenc.vorbis_encode_ctl.restype = c_int
+ libvorbisenc.vorbis_encode_ctl.argtypes = [vi_p, c_int, c_void_p]
+
+ def vorbis_encode_ctl(vi, number, arg):
+ return libvorbisenc.vorbis_encode_ctl(vi, number, arg)
+
+ class ovectl_ratemanage_arg(ctypes.Structure):
+ _fields_ = [("management_active", c_int),
+ ("bitrate_hard_min", c_long),
+ ("bitrate_hard_max", c_long),
+ ("bitrate_hard_window", c_double),
+ ("bitrate_av_lo", c_long),
+ ("bitrate_av_hi", c_long),
+ ("bitrate_av_window", c_double),
+ ("bitrate_av_window_center", c_double)]
+
+ class ovectl_ratemanage2_arg(ctypes.Structure):
+ _fields_ = [("management_active", c_int),
+ ("bitrate_limit_min_kbps", c_long),
+ ("bitrate_limit_max_kbps", c_long),
+ ("bitrate_limit_reservoir_bits", c_long),
+ ("bitrate_limit_reservoir_bias", c_double),
+ ("bitrate_average_kbps", c_long),
+ ("bitrate_average_damping", c_double)]
+
+ OV_ECTL_RATEMANAGE2_GET =0x14
+
+ OV_ECTL_RATEMANAGE2_SET =0x15
+
+ OV_ECTL_LOWPASS_GET =0x20
+
+ OV_ECTL_LOWPASS_SET =0x21
+
+ OV_ECTL_IBLOCK_GET =0x30
+
+ OV_ECTL_IBLOCK_SET =0x31
+
+ OV_ECTL_COUPLING_GET =0x40
+
+ OV_ECTL_COUPLING_SET =0x41
+
+ OV_ECTL_RATEMANAGE_GET =0x10
+
+ OV_ECTL_RATEMANAGE_SET =0x11
+
+ OV_ECTL_RATEMANAGE_AVG =0x12
+
+ OV_ECTL_RATEMANAGE_HARD =0x13
+ # end of vorbisenc
+ except:
+ pass
diff --git a/sbapp/pyogg/vorbis_file.py b/sbapp/pyogg/vorbis_file.py
new file mode 100644
index 00000000..918f1e8f
--- /dev/null
+++ b/sbapp/pyogg/vorbis_file.py
@@ -0,0 +1,161 @@
+import ctypes
+
+from . import vorbis
+from .audio_file import AudioFile
+from .pyogg_error import PyOggError
+
+# TODO: Issue #70: Vorbis files with multiple logical bitstreams could
+# be supported by chaining VorbisFile instances (with say a 'next'
+# attribute that points to the next VorbisFile that would contain the
+# PCM for the next logical bitstream). A considerable constraint to
+# implementing this was that examples files that demonstrated multiple
+# logical bitstreams couldn't be found or created. Note that even
+# Audacity doesn't handle multiple logical bitstreams (see
+# https://wiki.audacityteam.org/wiki/OGG#Importing_multiple_stream_files).
+
+# TODO: Issue #53: Unicode file names are not well supported.
+# They may work in macOS and Linux, they don't work under Windows.
+
+class VorbisFile(AudioFile):
+ def __init__(self,
+ path: str,
+ bytes_per_sample: int = 2,
+ signed:bool = True) -> None:
+ """Load an OggVorbis File.
+
+ path specifies the location of the Vorbis file. Unicode
+ filenames may not work correctly under Windows.
+
+ bytes_per_sample specifies the word size of the PCM. It may
+ be either 1 or 2. Specifying one byte per sample will save
+ memory but will likely decrease the quality of the decoded
+ audio.
+
+ Only Vorbis files with a single logical bitstream are
+ supported.
+
+ """
+ # Sanity check the number of bytes per sample
+ assert bytes_per_sample==1 or bytes_per_sample==2
+
+ # Sanity check that the vorbis library is available (for mypy)
+ assert vorbis.libvorbisfile is not None
+
+ #: Bytes per sample
+ self.bytes_per_sample = bytes_per_sample
+
+ #: Samples are signed (rather than unsigned)
+ self.signed = signed
+
+ # Create a Vorbis File structure
+ vf = vorbis.OggVorbis_File()
+
+ # Attempt to open the Vorbis file
+ error = vorbis.libvorbisfile.ov_fopen(
+ vorbis.to_char_p(path),
+ ctypes.byref(vf)
+ )
+
+ # Check for errors during opening
+ if error != 0:
+ raise PyOggError(
+ ("File '{}' couldn't be opened or doesn't exist. "+
+ "Error code : {}").format(path, error)
+ )
+
+ # Extract info from the Vorbis file
+ info = vorbis.libvorbisfile.ov_info(
+ ctypes.byref(vf),
+ -1 # the current logical bitstream
+ )
+
+ #: Number of channels in audio file.
+ self.channels = info.contents.channels
+
+ #: Number of samples per second (per channel), 44100 for
+ # example.
+ self.frequency = info.contents.rate
+
+ # Extract the total number of PCM samples for the first
+ # logical bitstream
+ pcm_length_samples = vorbis.libvorbisfile.ov_pcm_total(
+ ctypes.byref(vf),
+ 0 # to extract the length of the first logical bitstream
+ )
+
+ # Create a memory block to store the entire PCM
+ Buffer = (
+ ctypes.c_char
+ * (
+ pcm_length_samples
+ * self.bytes_per_sample
+ * self.channels
+ )
+ )
+ self.buffer = Buffer()
+
+ # Create a pointer to the newly allocated memory. It
+ # seems we can only do pointer arithmetic on void
+ # pointers. See
+ # https://mattgwwalker.wordpress.com/2020/05/30/pointer-manipulation-in-python/
+ buf_ptr = ctypes.cast(
+ ctypes.pointer(self.buffer),
+ ctypes.c_void_p
+ )
+
+ # Storage for the index of the logical bitstream
+ bitstream_previous = None
+ bitstream = ctypes.c_int()
+
+ # Set bytes remaining to read into PCM
+ read_size = len(self.buffer)
+
+ while True:
+ # Convert buffer pointer to the desired type
+ ptr = ctypes.cast(
+ buf_ptr,
+ ctypes.POINTER(ctypes.c_char)
+ )
+
+ # Attempt to decode PCM from the Vorbis file
+ result = vorbis.libvorbisfile.ov_read(
+ ctypes.byref(vf),
+ ptr,
+ read_size,
+ 0, # Little endian
+ self.bytes_per_sample,
+ int(self.signed),
+ ctypes.byref(bitstream)
+ )
+
+ # Check for errors
+ if result < 0:
+ raise PyOggError(
+ "An error occurred decoding the Vorbis file: "+
+ f"Error code: {result}"
+ )
+
+ # Check that the bitstream hasn't changed as we only
+ # support Vorbis files with a single logical bitstream.
+ if bitstream_previous is None:
+ bitstream_previous = bitstream
+ else:
+ if bitstream_previous != bitstream:
+ raise PyOggError(
+ "PyOgg currently supports Vorbis files "+
+ "with only one logical stream"
+ )
+
+ # Check for end of file
+ if result == 0:
+ break
+
+ # Calculate the number of bytes remaining to read into PCM
+ read_size -= result
+
+ # Update the pointer into the buffer
+ buf_ptr.value += result
+
+
+ # Close the file and clean up memory
+ vorbis.libvorbisfile.ov_clear(ctypes.byref(vf))
diff --git a/sbapp/pyogg/vorbis_file_stream.py b/sbapp/pyogg/vorbis_file_stream.py
new file mode 100644
index 00000000..57677ba7
--- /dev/null
+++ b/sbapp/pyogg/vorbis_file_stream.py
@@ -0,0 +1,110 @@
+import ctypes
+
+from . import vorbis
+from .pyogg_error import PyOggError
+
+class VorbisFileStream:
+ def __init__(self, path, buffer_size=8192):
+ self.exists = False
+ self._buffer_size = buffer_size
+
+ self.vf = vorbis.OggVorbis_File()
+ error = vorbis.ov_fopen(path, ctypes.byref(self.vf))
+ if error != 0:
+ raise PyOggError("file couldn't be opened or doesn't exist. Error code : {}".format(error))
+
+ info = vorbis.ov_info(ctypes.byref(self.vf), -1)
+
+ #: Number of channels in audio file.
+ self.channels = info.contents.channels
+
+ #: Number of samples per second (per channel). Always
+ # 48,000.
+ self.frequency = info.contents.rate
+
+ array = (ctypes.c_char*(self._buffer_size*self.channels))()
+
+ self.buffer_ = ctypes.cast(ctypes.pointer(array), ctypes.c_char_p)
+
+ self.bitstream = ctypes.c_int()
+ self.bitstream_pointer = ctypes.pointer(self.bitstream)
+
+ self.exists = True # TODO: is this the best place for this statement?
+
+ #: Bytes per sample
+ self.bytes_per_sample = 2 # TODO: Where is this defined?
+
+ def __del__(self):
+ if self.exists:
+ vorbis.ov_clear(ctypes.byref(self.vf))
+ self.exists = False
+
+ def clean_up(self):
+ vorbis.ov_clear(ctypes.byref(self.vf))
+
+ self.exists = False
+
+ def get_buffer(self):
+ """get_buffer() -> bytesBuffer, bufferLength
+
+ Returns None when all data has been read from the file.
+
+ """
+ if not self.exists:
+ return None
+ buffer = []
+ total_bytes_written = 0
+
+ while True:
+ new_bytes = vorbis.ov_read(ctypes.byref(self.vf), self.buffer_, self._buffer_size*self.channels - total_bytes_written, 0, 2, 1, self.bitstream_pointer)
+
+ array_ = ctypes.cast(self.buffer_, ctypes.POINTER(ctypes.c_char*(self._buffer_size*self.channels))).contents
+
+ buffer.append(array_.raw[:new_bytes])
+
+ total_bytes_written += new_bytes
+
+ if new_bytes == 0 or total_bytes_written >= self._buffer_size*self.channels:
+ break
+
+ out_buffer = b"".join(buffer)
+
+ if total_bytes_written == 0:
+ self.clean_up()
+ return(None)
+
+ return out_buffer
+
+ def get_buffer_as_array(self):
+ """Provides the buffer as a NumPy array.
+
+ Note that the underlying data type is 16-bit signed
+ integers.
+
+ Does not copy the underlying data, so the returned array
+ should either be processed or copied before the next call
+ to get_buffer() or get_buffer_as_array().
+
+ """
+ import numpy # type: ignore
+
+ # Read the next samples from the stream
+ buf = self.get_buffer()
+
+ # Check if we've come to the end of the stream
+ if buf is None:
+ return None
+
+ # Convert the bytes buffer to a NumPy array
+ array = numpy.frombuffer(
+ buf,
+ dtype=numpy.int16
+ )
+
+ # Reshape the array
+ return array.reshape(
+ (len(buf)
+ // self.bytes_per_sample
+ // self.channels,
+ self.channels)
+ )
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────